Merge bitcoin/bitcoin#32345: ipc: Handle unclean shutdowns better

2581258ec2 ipc: Handle bitcoin-wallet disconnections (Ryan Ofsky)
2160995916 ipc: Add Ctrl-C handler for spawned subprocesses (Ryan Ofsky)
0c28068ceb doc: Improve IPC interface comments (Ryan Ofsky)
7f65aac78b ipc: Avoid waiting for clients to disconnect when shutting down (Ryan Ofsky)
6eb09fd614 test: Add unit test coverage for Init and Shutdown code (Ryan Ofsky)
9a9fb19536 ipc: Use EventLoopRef instead of addClient/removeClient (Ryan Ofsky)
e886c65b6b Squashed 'src/ipc/libmultiprocess/' changes from 27c7e8e5a581..b4120d34bad2 (Ryan Ofsky)

Pull request description:

  This PR fixes various problems when IPC connections are broken or hang which were reported in https://github.com/bitcoin-core/libmultiprocess/issues/123, https://github.com/bitcoin-core/libmultiprocess/issues/176, and https://github.com/bitcoin-core/libmultiprocess/pull/182. The different fixes are described in commit messages.

  ---

  The first two commits of this PR update the libmultiprocess subtree including the following PRs:

  - https://github.com/bitcoin-core/libmultiprocess/pull/181
  - https://github.com/bitcoin-core/libmultiprocess/pull/179
  - https://github.com/bitcoin-core/libmultiprocess/pull/160
  - https://github.com/bitcoin-core/libmultiprocess/pull/184
  - https://github.com/bitcoin-core/libmultiprocess/pull/187
  - https://github.com/bitcoin-core/libmultiprocess/pull/186
  - https://github.com/bitcoin-core/libmultiprocess/pull/192

  The subtree changes can be verified by running `test/lint/git-subtree-check.sh src/ipc/libmultiprocess` as described in [developer notes](https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#subtrees) and [lint instructions](https://github.com/bitcoin/bitcoin/tree/master/test/lint#git-subtree-checksh).

  The remaining commits are:

  - [`9a9fb19536fa` ipc: Use EventLoopRef instead of addClient/removeClient](9a9fb19536)
  - [`6eb09fd6141f` test: Add unit test coverage for Init and Shutdown code](6eb09fd614)
  - [`7f65aac78b95` ipc: Avoid waiting for clients to disconnect when shutting down](7f65aac78b)
  - [`0c28068ceb7b` doc: Improve IPC interface comments](0c28068ceb)
  - [`216099591632` ipc: Add Ctrl-C handler for spawned subprocesses](2160995916)
  - [`2581258ec200` ipc: Handle bitcoin-wallet disconnections](2581258ec2)

  The new commits depend on the subtree update, and because the subtree update includes an incompatible API change, the "Use EventLoopRef" commit needs to be part of the same PR to avoid breaking the build. The other commits also make sense to merge at the same time because the bitcoin & libmultiprocess changes were written and tested together.

  ---

  This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/issues/28722).

ACKs for top commit:
  Sjors:
    re-utACK 2581258ec2
  josibake:
    code review ACK 2581258ec2
  pinheadmz:
    re-ACK 2581258ec2

Tree-SHA512: 0095aa22d507803e2a2d46eff51fb6caf965cc0c97ccfa615bd97805d5d51e66a5b4b040640deb92896438b1fb9f6879847124c9d0e120283287bfce37b8d748
This commit is contained in:
merge-script
2025-08-18 20:19:19 +01:00
78 changed files with 1119 additions and 477 deletions

View File

@@ -64,6 +64,7 @@ add_executable(test_bitcoin
net_peer_eviction_tests.cpp
net_tests.cpp
netbase_tests.cpp
node_init_tests.cpp
node_warnings_tests.cpp
orphanage_tests.cpp
pcp_tests.cpp

View File

@@ -55,7 +55,6 @@ void IpcPipeTest()
{
// Setup: create FooImplementation object and listen for FooInterface requests
std::promise<std::unique_ptr<mp::ProxyClient<gen::FooInterface>>> foo_promise;
std::function<void()> disconnect_client;
std::thread thread([&]() {
mp::EventLoop loop("IpcPipeTest", [](bool raise, const std::string& log) { LogInfo("LOG%i: %s", raise, log); });
auto pipe = loop.m_io_context.provider->newTwoWayPipe();
@@ -63,9 +62,9 @@ void IpcPipeTest()
auto connection_client = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[0]));
auto foo_client = std::make_unique<mp::ProxyClient<gen::FooInterface>>(
connection_client->m_rpc_system->bootstrap(mp::ServerVatId().vat_id).castAs<gen::FooInterface>(),
connection_client.get(), /* destroy_connection= */ false);
connection_client.get(), /* destroy_connection= */ true);
connection_client.release();
foo_promise.set_value(std::move(foo_client));
disconnect_client = [&] { loop.sync([&] { connection_client.reset(); }); };
auto connection_server = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]), [&](mp::Connection& connection) {
auto foo_server = kj::heap<mp::ProxyServer<gen::FooInterface>>(std::make_shared<FooImplementation>(), connection);
@@ -106,8 +105,8 @@ void IpcPipeTest()
auto script2{foo->passScript(script1)};
BOOST_CHECK_EQUAL(HexStr(script1), HexStr(script2));
// Test cleanup: disconnect pipe and join thread
disconnect_client();
// Test cleanup: disconnect and join thread
foo.reset();
thread.join();
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2025 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <init.h>
#include <interfaces/init.h>
#include <rpc/server.h>
#include <boost/test/unit_test.hpp>
#include <test/util/setup_common.h>
using node::NodeContext;
BOOST_FIXTURE_TEST_SUITE(node_init_tests, BasicTestingSetup)
//! Custom implementation of interfaces::Init for testing.
class TestInit : public interfaces::Init
{
public:
TestInit(NodeContext& node) : m_node(node)
{
InitContext(m_node);
m_node.init = this;
}
std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
std::unique_ptr<interfaces::WalletLoader> makeWalletLoader(interfaces::Chain& chain) override
{
return MakeWalletLoader(chain, *Assert(m_node.args));
}
NodeContext& m_node;
};
BOOST_AUTO_TEST_CASE(init_test)
{
// Clear state set by BasicTestingSetup that AppInitMain assumes is unset.
LogInstance().DisconnectTestLogger();
m_node.args->SetConfigFilePath({});
// Prevent the test from trying to listen on ports 8332 and 8333.
m_node.args->ForceSetArg("-server", "0");
m_node.args->ForceSetArg("-listen", "0");
// Run through initialization and shutdown code.
TestInit init{m_node};
BOOST_CHECK(AppInitInterfaces(m_node));
BOOST_CHECK(AppInitMain(m_node));
Interrupt(m_node);
Shutdown(m_node);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -116,6 +116,14 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, TestOpts opts)
if (!EnableFuzzDeterminism()) {
SeedRandomForTest(SeedRand::FIXED_SEED);
}
// Reset globals
fDiscover = true;
fListen = true;
SetRPCWarmupStarting();
g_reachable_nets.Reset();
ClearLocal();
m_node.shutdown_signal = &m_interrupt;
m_node.shutdown_request = [this]{ return m_interrupt(); };
m_node.args = &gArgs;
@@ -215,7 +223,10 @@ BasicTestingSetup::~BasicTestingSetup()
} else {
fs::remove_all(m_path_root);
}
// Clear all arguments except for -datadir, which GUI tests currently rely
// on to be set even after the testing setup is destroyed.
gArgs.ClearArgs();
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
}
ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)