Skip to content

Commit ac219dc

Browse files
committed
Merge bitcoin/bitcoin#19160: multiprocess: Add basic spawn and IPC support
84934bf multiprocess: Add echoipc RPC method and test (Russell Yanofsky) 7d76cf6 multiprocess: Add comments and documentation (Russell Yanofsky) ddf7ecc multiprocess: Add bitcoin-node process spawning support (Russell Yanofsky) 10afdf0 multiprocess: Add Ipc interface implementation (Russell Yanofsky) 745c9ce multiprocess: Add Ipc and Init interface definitions (Russell Yanofsky) 5d62d7f Update libmultiprocess library (Russell Yanofsky) Pull request description: This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/projects/10). --- This PR adds basic process spawning and IPC method call support to `bitcoin-node` executables built with `--enable-multiprocess`[*]. These changes are used in bitcoin/bitcoin#10102 to let node, gui, and wallet functionality run in different processes, and extended in bitcoin/bitcoin#19460 and bitcoin/bitcoin#19461 after that to allow gui and wallet processes to be started and stopped independently and connect to the node over a socket. These changes can also be used to implement new functionality outside the `bitcoin-node` process like external indexes or pluggable transports (bitcoin/bitcoin#18988). The `Ipc::spawnProcess` and `Ipc::serveProcess` methods added here are entry points for spawning a child process and serving a parent process, and being able to make bidirectional, multithreaded method calls between the processes. A simple example of this is implemented in commit "Add echoipc RPC method and test." Changes in this PR aside from the echo test were originally part of #10102, but have been split and moved here for easier review, and so they can be used for other applications like external plugins. Additional notes about this PR can be found at https://bitcoincore.reviews/19160 [*] Note: the `--enable-multiprocess` feature is still experimental, and not enabled by default, and not yet supported on windows. More information can be found in [doc/multiprocess.md](https://github.com/bitcoin/bitcoin/blob/master/doc/multiprocess.md) ACKs for top commit: fjahr: re-ACK 84934bf ariard: ACK 84934bf. Changes since last ACK fixes the silent merge conflict about `EnsureAnyNodeContext()`. Rebuilt and checked again debug command `echoipc`. Tree-SHA512: 52a948b5e18a26d7d7a09b83003eaae9b1ed2981978c36c959fe9a55abf70ae6a627c4ff913a3428be17400a3dace30c58b5057fa75c319662c3be98f19810c6
2 parents 19a56d1 + 84934bf commit ac219dc

30 files changed

+805
-13
lines changed

build_msvc/bitcoind/bitcoind.vcxproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
</PropertyGroup>
1111
<ItemGroup>
1212
<ClCompile Include="..\..\src\bitcoind.cpp" />
13+
<ClCompile Include="..\..\src\init\bitcoind.cpp">
14+
<ObjectFileName>$(IntDir)init_bitcoind.obj</ObjectFileName>
15+
</ClCompile>
1316
</ItemGroup>
1417
<ItemGroup>
1518
<ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">

depends/packages/native_libmultiprocess.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package=native_libmultiprocess
2-
$(package)_version=5741d750a04e644a03336090d8979c6d033e32c0
2+
$(package)_version=d576d975debdc9090bd2582f83f49c76c0061698
33
$(package)_download_path=https://github.com/chaincodelabs/libmultiprocess/archive
44
$(package)_file_name=$($(package)_version).tar.gz
5-
$(package)_sha256_hash=ac848db49a6ed53e423c62d54bd87f1f08cbb0326254a8667e10bbfe5bf032a4
5+
$(package)_sha256_hash=9f8b055c8bba755dc32fe799b67c20b91e7b13e67cadafbc54c0f1def057a370
66
$(package)_dependencies=native_capnp
77

88
define $(package)_config_cmds

doc/multiprocess.md

Lines changed: 38 additions & 1 deletion
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/Makefile.am

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ EXTRA_LIBRARIES += \
7474
$(LIBBITCOIN_CONSENSUS) \
7575
$(LIBBITCOIN_SERVER) \
7676
$(LIBBITCOIN_CLI) \
77+
$(LIBBITCOIN_IPC) \
7778
$(LIBBITCOIN_WALLET) \
7879
$(LIBBITCOIN_WALLET_TOOL) \
7980
$(LIBBITCOIN_ZMQ)
@@ -158,7 +159,10 @@ BITCOIN_CORE_H = \
158159
init.h \
159160
init/common.h \
160161
interfaces/chain.h \
162+
interfaces/echo.h \
161163
interfaces/handler.h \
164+
interfaces/init.h \
165+
interfaces/ipc.h \
162166
interfaces/node.h \
163167
interfaces/wallet.h \
164168
key.h \
@@ -299,6 +303,8 @@ obj/build.h: FORCE
299303
"$(abs_top_srcdir)"
300304
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
301305

306+
ipc/capnp/libbitcoin_ipc_a-ipc.$(OBJEXT): $(libbitcoin_ipc_mpgen_input:=.h)
307+
302308
# server: shared between bitcoind and bitcoin-qt
303309
# Contains code accessing mempool and chain state that is meant to be separated
304310
# from wallet and gui code (see node/README.md). Shared code should go in
@@ -558,7 +564,9 @@ libbitcoin_util_a_SOURCES = \
558564
compat/glibcxx_sanity.cpp \
559565
compat/strnlen.cpp \
560566
fs.cpp \
567+
interfaces/echo.cpp \
561568
interfaces/handler.cpp \
569+
interfaces/init.cpp \
562570
logging.cpp \
563571
random.cpp \
564572
randomenv.cpp \
@@ -634,17 +642,17 @@ bitcoin_bin_ldadd = \
634642

635643
bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS)
636644

637-
bitcoind_SOURCES = $(bitcoin_daemon_sources)
645+
bitcoind_SOURCES = $(bitcoin_daemon_sources) init/bitcoind.cpp
638646
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
639647
bitcoind_CXXFLAGS = $(bitcoin_bin_cxxflags)
640648
bitcoind_LDFLAGS = $(bitcoin_bin_ldflags)
641649
bitcoind_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
642650

643-
bitcoin_node_SOURCES = $(bitcoin_daemon_sources)
651+
bitcoin_node_SOURCES = $(bitcoin_daemon_sources) init/bitcoin-node.cpp
644652
bitcoin_node_CPPFLAGS = $(bitcoin_bin_cppflags)
645653
bitcoin_node_CXXFLAGS = $(bitcoin_bin_cxxflags)
646654
bitcoin_node_LDFLAGS = $(bitcoin_bin_ldflags)
647-
bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
655+
bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd) $(LIBBITCOIN_IPC) $(LIBMULTIPROCESS_LIBS)
648656

649657
# bitcoin-cli binary #
650658
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -808,6 +816,39 @@ if HARDEN
808816
$(AM_V_at) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
809817
endif
810818

819+
libbitcoin_ipc_mpgen_input = \
820+
ipc/capnp/echo.capnp \
821+
ipc/capnp/init.capnp
822+
EXTRA_DIST += $(libbitcoin_ipc_mpgen_input)
823+
%.capnp:
824+
825+
if BUILD_MULTIPROCESS
826+
LIBBITCOIN_IPC=libbitcoin_ipc.a
827+
libbitcoin_ipc_a_SOURCES = \
828+
ipc/capnp/init-types.h \
829+
ipc/capnp/protocol.cpp \
830+
ipc/capnp/protocol.h \
831+
ipc/exception.h \
832+
ipc/interfaces.cpp \
833+
ipc/process.cpp \
834+
ipc/process.h \
835+
ipc/protocol.h
836+
libbitcoin_ipc_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
837+
libbitcoin_ipc_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(LIBMULTIPROCESS_CFLAGS)
838+
839+
include $(MPGEN_PREFIX)/include/mpgen.mk
840+
libbitcoin_ipc_mpgen_output = \
841+
$(libbitcoin_ipc_mpgen_input:=.c++) \
842+
$(libbitcoin_ipc_mpgen_input:=.h) \
843+
$(libbitcoin_ipc_mpgen_input:=.proxy-client.c++) \
844+
$(libbitcoin_ipc_mpgen_input:=.proxy-server.c++) \
845+
$(libbitcoin_ipc_mpgen_input:=.proxy-types.c++) \
846+
$(libbitcoin_ipc_mpgen_input:=.proxy-types.h) \
847+
$(libbitcoin_ipc_mpgen_input:=.proxy.h)
848+
nodist_libbitcoin_ipc_a_SOURCES = $(libbitcoin_ipc_mpgen_output)
849+
CLEANFILES += $(libbitcoin_ipc_mpgen_output)
850+
endif
851+
811852
if EMBEDDED_LEVELDB
812853
include Makefile.crc32c.include
813854
include Makefile.leveldb.include

src/bitcoind.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <compat.h>
1313
#include <init.h>
1414
#include <interfaces/chain.h>
15+
#include <interfaces/init.h>
1516
#include <node/context.h>
1617
#include <node/ui_interface.h>
1718
#include <noui.h>
@@ -104,10 +105,8 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
104105

105106
#endif
106107

107-
static bool AppInit(int argc, char* argv[])
108+
static bool AppInit(NodeContext& node, int argc, char* argv[])
108109
{
109-
NodeContext node;
110-
111110
bool fRet = false;
112111

113112
util::ThreadSetInternalName("init");
@@ -254,10 +253,18 @@ int main(int argc, char* argv[])
254253
util::WinCmdLineArgs winArgs;
255254
std::tie(argc, argv) = winArgs.get();
256255
#endif
256+
257+
NodeContext node;
258+
int exit_status;
259+
std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
260+
if (!init) {
261+
return exit_status;
262+
}
263+
257264
SetupEnvironment();
258265

259266
// Connect bitcoind signal handlers
260267
noui_connect();
261268

262-
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
269+
return (AppInit(node, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
263270
}

src/init/bitcoin-node.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <interfaces/echo.h>
6+
#include <interfaces/init.h>
7+
#include <interfaces/ipc.h>
8+
#include <node/context.h>
9+
10+
#include <memory>
11+
12+
namespace init {
13+
namespace {
14+
const char* EXE_NAME = "bitcoin-node";
15+
16+
class BitcoinNodeInit : public interfaces::Init
17+
{
18+
public:
19+
BitcoinNodeInit(NodeContext& node, const char* arg0)
20+
: m_node(node),
21+
m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
22+
{
23+
m_node.init = this;
24+
}
25+
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
26+
interfaces::Ipc* ipc() override { return m_ipc.get(); }
27+
NodeContext& m_node;
28+
std::unique_ptr<interfaces::Ipc> m_ipc;
29+
};
30+
} // namespace
31+
} // namespace init
32+
33+
namespace interfaces {
34+
std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status)
35+
{
36+
auto init = std::make_unique<init::BitcoinNodeInit>(node, argc > 0 ? argv[0] : "");
37+
// Check if bitcoin-node is being invoked as an IPC server. If so, then
38+
// bypass normal execution and just respond to requests over the IPC
39+
// channel and return null.
40+
if (init->m_ipc->startSpawnedProcess(argc, argv, exit_status)) {
41+
return nullptr;
42+
}
43+
return init;
44+
}
45+
} // namespace interfaces

src/init/bitcoind.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <interfaces/init.h>
6+
#include <node/context.h>
7+
8+
#include <memory>
9+
10+
namespace init {
11+
namespace {
12+
class BitcoindInit : public interfaces::Init
13+
{
14+
public:
15+
BitcoindInit(NodeContext& node) : m_node(node)
16+
{
17+
m_node.init = this;
18+
}
19+
NodeContext& m_node;
20+
};
21+
} // namespace
22+
} // namespace init
23+
24+
namespace interfaces {
25+
std::unique_ptr<Init> MakeNodeInit(NodeContext& node, int argc, char* argv[], int& exit_status)
26+
{
27+
return std::make_unique<init::BitcoindInit>(node);
28+
}
29+
} // namespace interfaces

src/interfaces/README.md

Lines changed: 4 additions & 2 deletions
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/echo.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <interfaces/echo.h>
6+
7+
#include <memory>
8+
9+
namespace interfaces {
10+
namespace {
11+
class EchoImpl : public Echo
12+
{
13+
public:
14+
std::string echo(const std::string& echo) override { return echo; }
15+
};
16+
} // namespace
17+
std::unique_ptr<Echo> MakeEcho() { return std::make_unique<EchoImpl>(); }
18+
} // namespace interfaces

src/interfaces/echo.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_INTERFACES_ECHO_H
6+
#define BITCOIN_INTERFACES_ECHO_H
7+
8+
#include <memory>
9+
#include <string>
10+
11+
namespace interfaces {
12+
//! Simple string echoing interface for testing.
13+
class Echo
14+
{
15+
public:
16+
virtual ~Echo() {}
17+
18+
//! Echo provided string.
19+
virtual std::string echo(const std::string& echo) = 0;
20+
};
21+
22+
//! Return implementation of Echo interface.
23+
std::unique_ptr<Echo> MakeEcho();
24+
} // namespace interfaces
25+
26+
#endif // BITCOIN_INTERFACES_ECHO_H

src/interfaces/init.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <interfaces/chain.h>
6+
#include <interfaces/echo.h>
7+
#include <interfaces/init.h>
8+
#include <interfaces/node.h>
9+
#include <interfaces/wallet.h>
10+
11+
namespace interfaces {
12+
std::unique_ptr<Node> Init::makeNode() { return {}; }
13+
std::unique_ptr<Chain> Init::makeChain() { return {}; }
14+
std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; }
15+
std::unique_ptr<Echo> Init::makeEcho() { return {}; }
16+
Ipc* Init::ipc() { return nullptr; }
17+
} // namespace interfaces

0 commit comments

Comments
 (0)