Skip to content

Commit 7d76cf6

Browse files
committed
multiprocess: Add comments and documentation
1 parent ddf7ecc commit 7d76cf6

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

doc/multiprocess.md

+38-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Specific next steps after [#10102](https://github.com/bitcoin/bitcoin/pull/10102
1515

1616
## Debugging
1717

18-
After [#10102](https://github.com/bitcoin/bitcoin/pull/10102), the `-debug=ipc` command line option can be used to see requests and responses between processes.
18+
The `-debug=ipc` command line option can be used to see requests and responses between processes.
1919

2020
## Installation
2121

@@ -33,3 +33,40 @@ BITCOIND=bitcoin-node test/functional/test_runner.py
3333
The configure script will pick up settings and library locations from the depends directory, so there is no need to pass `--enable-multiprocess` as a separate flag when using the depends system (it's controlled by the `MULTIPROCESS=1` option).
3434

3535
Alternately, you can install [Cap'n Proto](https://capnproto.org/) and [libmultiprocess](https://github.com/chaincodelabs/libmultiprocess) packages on your system, and just run `./configure --enable-multiprocess` without using the depends system. The configure script will be able to locate the installed packages via [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/). See [Installation](https://github.com/chaincodelabs/libmultiprocess#installation) section of the libmultiprocess readme for install steps. See [build-unix.md](build-unix.md) and [build-osx.md](build-osx.md) for information about installing dependencies in general.
36+
37+
## IPC implementation details
38+
39+
Cross process Node, Wallet, and Chain interfaces are defined in
40+
[`src/interfaces/`](../src/interfaces/). These are C++ classes which follow
41+
[conventions](developer-notes.md#internal-interface-guidelines), like passing
42+
serializable arguments so they can be called from different processes, and
43+
making methods pure virtual so they can have proxy implementations that forward
44+
calls between processes.
45+
46+
When Wallet, Node, and Chain code is running in the same process, calling any
47+
interface method invokes the implementation directly. When code is running in
48+
different processes, calling an interface method invokes a proxy interface
49+
implementation that communicates with a remote process and invokes the real
50+
implementation in the remote process. The
51+
[libmultiprocess](https://github.com/chaincodelabs/libmultiprocess) code
52+
generation tool internally generates proxy client classes and proxy server
53+
classes for this purpose that are thin wrappers around Cap'n Proto
54+
[client](https://capnproto.org/cxxrpc.html#clients) and
55+
[server](https://capnproto.org/cxxrpc.html#servers) classes, which handle the
56+
actual serialization and socket communication.
57+
58+
As much as possible, calls between processes are meant to work the same as
59+
calls within a single process without adding limitations or requiring extra
60+
implementation effort. Processes communicate with each other by calling regular
61+
[C++ interface methods](../src/interfaces/README.md). Method arguments and
62+
return values are automatically serialized and sent between processes. Object
63+
references and `std::function` arguments are automatically tracked and mapped
64+
to allow invoked code to call back into invoking code at any time, and there is
65+
a 1:1 threading model where any thread invoking a method in another process has
66+
a corresponding thread in the invoked process responsible for executing all
67+
method calls from the source thread, without blocking I/O or holding up another
68+
call, and using the same thread local variables, locks, and callbacks between
69+
calls. The forwarding, tracking, and threading is implemented inside the
70+
[libmultiprocess](https://github.com/chaincodelabs/libmultiprocess) library
71+
which has the design goal of making calls between processes look like calls in
72+
the same process to the extent possible.

src/interfaces/README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ The following interfaces are defined here:
1212

1313
* [`Handler`](handler.h) — returned by `handleEvent` methods on interfaces above and used to manage lifetimes of event handlers.
1414

15-
* [`Init`](init.h) — used by multiprocess code to access interfaces above on startup. Added in [#10102](https://github.com/bitcoin/bitcoin/pull/10102).
15+
* [`Init`](init.h) — used by multiprocess code to access interfaces above on startup. Added in [#19160](https://github.com/bitcoin/bitcoin/pull/19160).
1616

17-
The interfaces above define boundaries between major components of bitcoin code (node, wallet, and gui), making it possible for them to run in different processes, and be tested, developed, and understood independently. These interfaces are not currently designed to be stable or to be used externally.
17+
* [`Ipc`](ipc.h) — used by multiprocess code to access `Init` interface across processes. Added in [#19160](https://github.com/bitcoin/bitcoin/pull/19160).
18+
19+
The interfaces above define boundaries between major components of bitcoin code (node, wallet, and gui), making it possible for them to run in [different processes](../../doc/multiprocess.md), and be tested, developed, and understood independently. These interfaces are not currently designed to be stable or to be used externally.

src/interfaces/ipc.h

+24-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,30 @@ namespace interfaces {
1313
class Init;
1414

1515
//! Interface providing access to interprocess-communication (IPC)
16-
//! functionality.
16+
//! functionality. The IPC implementation is responsible for establishing
17+
//! connections between a controlling process and a process being controlled.
18+
//! When a connection is established, the process being controlled returns an
19+
//! interfaces::Init pointer to the controlling process, which the controlling
20+
//! process can use to get access to other interfaces and functionality.
21+
//!
22+
//! When spawning a new process, the steps are:
23+
//!
24+
//! 1. The controlling process calls interfaces::Ipc::spawnProcess(), which
25+
//! calls ipc::Process::spawn(), which spawns a new process and returns a
26+
//! socketpair file descriptor for communicating with it.
27+
//! interfaces::Ipc::spawnProcess() then calls ipc::Protocol::connect()
28+
//! passing the socketpair descriptor, which returns a local proxy
29+
//! interfaces::Init implementation calling remote interfaces::Init methods.
30+
//! 2. The spawned process calls interfaces::Ipc::startSpawnProcess(), which
31+
//! calls ipc::Process::checkSpawned() to read command line arguments and
32+
//! determine whether it is a spawned process and what socketpair file
33+
//! descriptor it should use. It then calls ipc::Protocol::serve() to handle
34+
//! incoming requests from the socketpair and invoke interfaces::Init
35+
//! interface methods, and exit when the socket is closed.
36+
//! 3. The controlling process calls local proxy interfaces::Init object methods
37+
//! to make other proxy objects calling other remote interfaces. It can also
38+
//! destroy the initial interfaces::Init object to close the connection and
39+
//! shut down the spawned process.
1740
class Ipc
1841
{
1942
public:

0 commit comments

Comments
 (0)