Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion include/mp/proxy-io.h
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,12 @@ ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client cli
// the remote object, waiting for it to be deleted server side. If the
// capnp interface does not define a destroy method, this will just call
// an empty stub defined in the ProxyClientBase class and do nothing.
Sub::destroy(*this);
// Exceptions are caught and logged rather than propagated because
// ~ProxyClientBase is noexcept and the peer may be gone by the time
// this runs.
if (kj::runCatchingExceptions([&]{ Sub::destroy(*this); }) != nullptr) {
MP_LOG(*m_context.loop, Log::Warning) << "Remote destroy call failed during cleanup. Continuing.";
}

// FIXME: Could just invoke removed addCleanup fn here instead of duplicating code
m_context.loop->sync([&]() {
Expand Down
26 changes: 26 additions & 0 deletions test/mp/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,32 @@ KJ_TEST("Calling async IPC method, with server disconnect after cleanup")
}
}

KJ_TEST("Destroying ProxyClient<> with destroy method after peer disconnect")
{
// Regression test for bitcoin-core/libmultiprocess#219 where
// ~ProxyClientBase would call std::terminate if the remote destroy RPC
// failed during teardown.
//
// Save a callback on the server so it holds a ProxyClient<FooCallback>
// pointing back to this side, then disconnect. When the server is torn
// down, the ProxyClient<FooCallback> destructor issues a destroy RPC over
// the now dead connection; without the bugfix the exception escapes the
// noexcept destructor and aborts the process.

TestSetup setup{/*client_owns_connection=*/false};
ProxyClient<messages::FooInterface>* foo = setup.client.get();
foo->initThreadMap();

class Callback : public FooCallback
{
public:
int call(int arg) override { return arg; }
};

foo->saveCallback(std::make_shared<Callback>());
setup.client_disconnect();
}

KJ_TEST("Make simultaneous IPC calls on single remote thread")
{
TestSetup setup;
Expand Down
Loading