This PR contains an attempt to improve the design documentation to help new contributors to the repo. It adds more details about BuildField, ReadField and PassField. It explains how Callbacks, ThreadMaps and Async processing work.
Design Documentation Update #229
pull Eunovo wants to merge 1 commits into bitcoin-core:master from Eunovo:design-doc changing 1 files +185 −2-
Eunovo commented at 7:42 AM on October 22, 2025: contributor
-
DrahtBot commented at 7:42 AM on October 22, 2025: none
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--021abf342d371248e50ceaed478a90ca-->
Reviews
See the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
<!--5faf32d7da4f0f540f40219e4f7537a3-->
LLM Linter (✨ experimental)
Possible typos and grammar issues:
- resuse -> reuse [spelling error]
- capabilites -> capabilities [spelling error]
<sup>drahtbot_id_5_m</sup>
- Eunovo force-pushed on Oct 22, 2025
- Eunovo force-pushed on Oct 22, 2025
-
enirox001 commented at 6:26 PM on October 23, 2025: none
This is a helpful update. As someone getting familiar with this, the new details really helped clarify the design.
-
Sjors commented at 10:10 AM on October 24, 2025: member
Concept ACK on expanding documentation, and thanks for adding these graphics.
Reviewer hint, switch to the rich diff:
<img width="1247" height="310" alt="Schermafbeelding 2025-10-24 om 12 10 06" src="https://github.com/user-attachments/assets/8eaa467e-cfb8-49ad-b60c-009ee25da5b2" /> @ryanofsky is probably best qualified to check correctness.
-
in doc/design.md:46 in 459a730fe1
42 | + 43 | + Note over clientInvoke,serverInvoke: Input Parameter Flow 44 | + clientInvoke->>BuildField: BuildField(input_arg) 45 | + BuildField->>Socket: Serialize input 46 | + Socket->>ReadField: Cap'n Proto message 47 | + ReadField->>serverInvoke: Deserialize input
ryanofsky commented at 1:58 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
I think this is reasonable but one thing that's a little off about this is it makes it seem like
serverInvokecall is happening after theReadFieldcall, while theserverInvokecall is really the initial entry point on the server, andserverInvokecallsReadFieldto read method inputs, then executes the method, then calls thenBuildFieldto send method outputs. I think to fix this, I'd tweak participant list in the diagram to look like:- clientInvoke
- BuildField (client)
- ReadField (client)
- Request message
- serverInvoke
- ReadField (server)
- BuildField (server)
- Response message
Moving
serverInvokeparticipant beforeReadFieldand replacing singleSocketcolumn with separateRequestandResponsecolumns.Other fixes could be possible too, this is just what comes to mind.
in doc/design.md:61 in 459a730fe1
57 | + 58 | +Parameters are represented as Fields that must be set on Cap'n Proto Builder objects (for sending) and read from Reader objects (for receiving). 59 | + 60 | +#### Building Fields 61 | + 62 | +`BuildField` uses a `StructField` (identifying which field by index) and a parameter `Accessor` to set the appropriate field in the Cap'n Proto Builder object.
ryanofsky commented at 2:09 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
I think this section is basically right but it should drop the reference to
StructFieldand just showBuildFieldusing theAccessordirectly, rather than going throughStructField.The
StructFieldclass is not actually relevant in most cases and is only used to help serialize certain structs likeBlockRefwhich are using$Proxy.wrapannotation to automatically copy between C++ structs and Cap'n Proto structs that have the same fields.Such structs are used in a few places, but are not very common. The use of
Proxy.wrapfor serializing structs is documented herein doc/design.md:87 in 459a730fe1
83 | + 84 | +The same process is used for building results on the server side with `Results::Builder`. 85 | + 86 | +#### Reading Fields 87 | + 88 | +`ReadField` uses a `StructField` (identifying which field by index) and a parameter `Accessor` to read the appropriate field from the Cap'n Proto Reader object and reconstruct C++ parameters.
ryanofsky commented at 2:18 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
Again this should drop the reference to
StructFieldand just showReadFieldcalling theAccessordirectly rather than going throughStructFieldin doc/design.md:65 in 459a730fe1
61 | + 62 | +`BuildField` uses a `StructField` (identifying which field by index) and a parameter `Accessor` to set the appropriate field in the Cap'n Proto Builder object. 63 | + 64 | +```mermaid 65 | +sequenceDiagram 66 | + participant clientInvoke
ryanofsky commented at 2:21 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
I think this would more accurately say "clientInvoke or serverInvoke" instead of just "clientInvoke".
BuildFieldis used byclientInvokeon the client side to build Cap'n Proto request messages from C++ function parameters. But it is also used byserverInvokeon the server side to build Cap'n Proto response messages from C++ return values and output parameters and exceptions.in doc/design.md:91 in 459a730fe1
87 | + 88 | +`ReadField` uses a `StructField` (identifying which field by index) and a parameter `Accessor` to read the appropriate field from the Cap'n Proto Reader object and reconstruct C++ parameters. 89 | + 90 | +```mermaid 91 | +sequenceDiagram 92 | + participant serverInvoke
ryanofsky commented at 2:24 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
Again this could say "clientInvoke or serverInvoke" instead of just "serverInvoke" since
ReadFieldis used by theserverInvoketo read C++ input parameters from the request message, but it is also used byclientInvoketo read C++ return values and output parameters and exception information from the response message.in doc/design.md:92 in 459a730fe1
88 | +`ReadField` uses a `StructField` (identifying which field by index) and a parameter `Accessor` to read the appropriate field from the Cap'n Proto Reader object and reconstruct C++ parameters. 89 | + 90 | +```mermaid 91 | +sequenceDiagram 92 | + participant serverInvoke 93 | + participant PassField
ryanofsky commented at 2:40 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
I think I would drop reference to
PassFieldcolumn from this diagram and just showReadFieldbeing called directly here byclientInvokeandserverInvoke.The
PassFieldfunction is mostly an implementation detail of theserverInvokefunction and is really just wrapper forReadFieldandBuildFieldcalled for each C++ parameter, and is responsible for skipping the BuildField call for input-only parameters, and skipping the ReadField call for output-only parameters.PassFieldis actually covered pretty well in the next section ("Server-Side Request Processing"), so it seems fine not to include in this section.Reason I think
ReadFieldandBuildFieldare more useful to mention thanPassFieldis that while it is possible for libmultiprocess applications to provideCustomPassFieldfunctions like they can provideCustomReadFieldandCustomBuildFieldfunctions for custom parameter handling, this is rarely needed and overridingCustomReadFieldandCustomBuildFieldis more useful and more common.in doc/design.md:179 in 459a730fe1
175 | +Thread mapping enables each client thread to have a dedicated server thread processing its requests, preserving thread-local state and allowing recursive mutex usage across process boundaries. 176 | + 177 | +Thread mapping is initialized by defining an interface method with a `ThreadMap` parameter and/or response, such as: 178 | + 179 | +```capnp 180 | +initThreadMap @4 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
ryanofsky commented at 2:56 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
I think I'd tweak this example a little to replace
initThreadMap [@4](/bitcoin-core-multiprocess/contributor/4/)withconstruct [@0](/bitcoin-core-multiprocess/contributor/0/)because while it is possible to pass ThreadMap references in an manual method call like this, it usually makes sense to pass them automatically, as soon as the connection is created, by passing them in the init interfaceconstructmethod which is called automatically by the libmultiprocess c++ client when the client proxy object is constructed.The current unit tests do use a manual
initThreadMapmethod for some manual testing, but if you look at the example code or bitcoin core code, they useInit.constructmethods instead, so ThreadMaps are exchanged automatically:in doc/design.md:204 in 459a730fe1
200 | + 201 | +When a method has a `Context` parameter: 202 | + 203 | +**Client side** (`CustomBuildField`): 204 | +1. Creates a local `Thread::Server` object for the current thread (stored in `callback_threads` map) 205 | +2. Calls `connection.m_thread_map.makeThreadRequest()` to request a dedicated worker thread on the server (stored in `request_threads` map)
ryanofsky commented at 3:01 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
It would be good to be more explicit that a new server thread is created only when the current client thread is making an IPC call to this server for the very first time.
If this client thread has made any IPC calls to the server previously, it will reuse the thread that it first created and stored in the
request_threadsmap.It would also be good to say that the remote thread object is set in the Context.thread field sent in the request.
in doc/design.md:203 in 459a730fe1
199 | +``` 200 | + 201 | +When a method has a `Context` parameter: 202 | + 203 | +**Client side** (`CustomBuildField`): 204 | +1. Creates a local `Thread::Server` object for the current thread (stored in `callback_threads` map)
ryanofsky commented at 3:10 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
Few suggestions:
Would probably switch order of (1) and (2) in this list since primary purpose of the context struct is to allow cilent to specify threads for the server to execute calls on by setting the
Context.threadfield. TheContext.callbackThreadfield is also useful to let the server call back into the calling thread, so things like recursive mutexes work as expected, but this functionality is not used as much.Would be nice to mention the local
Thread::Serverreference is saved in theContext.callbackThreadfield of the context struct passed to the server, in addition to being saved in thecallback_threadsmaps.Would be nice to mention a new
Thread::Serveris only created the first time this thread makes an IPC call to the server. After that thecallback_threadsmap value is reused.
in doc/design.md:185 in 459a730fe1 outdated
190 | + 191 | +When both parameter and response include ThreadMap, both processes end up with `ThreadMap::Client` capabilities pointing to each other's `ThreadMap::Server`, allowing both sides to create threads on the other process. 192 | + 193 | +### Async Processing with Context 194 | + 195 | +By adding a `Context` parameter to a method in the capnp interface file, you enable async processing where the client tells the server to execute the request in a separate worker thread. For example:
ryanofsky commented at 3:19 PM on October 27, 2025:In commit "Design doc update" (459a730fe1689ebbad827d066f3180ffbbf7bd57)
Would be good to say that if a method does not have a
Contextparameter, then libmultiprocess will execute IPC requests invoking that method on the I/O event loop thread.This is ok if the method is fast and non-blocking, but should be avoided if the method is slow or blocks waiting for resources, or makes any IPC calls (including callbacks to the client), since as long as the method is executing, the Cap'n Proto event loop will not be able to perform any I/O.
ryanofsky approvedryanofsky commented at 3:36 PM on October 27, 2025: collaboratorCode review ACK 459a730fe1689ebbad827d066f3180ffbbf7bd57. These updates are very good, and I think information about callbacks and context parameters should be especially helpful. I left a number of suggestions below, which can be addressed here or in a followup. You can let me know which you prefer.
Eunovo force-pushed on Oct 28, 2025Design doc update cc234be73aEunovo force-pushed on Oct 28, 2025Eunovo commented at 8:08 PM on October 28, 2025: contributorThanks for the review @ryanofsky . I made the suggested changes.
ryanofsky approvedryanofsky commented at 12:48 PM on November 3, 2025: collaboratorCode review ACK cc234be73a68bc6cbf4940742dbe924234810f5d. Thanks for the updates! All the descriptions seem accurate now, and hopefully the diagrams make it clear how the different classes fit together
ryanofsky merged this on Nov 3, 2025ryanofsky closed this on Nov 3, 2025
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin-core/libmultiprocess. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-20 18:30 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me