Skip to content

Commit 61de697

Browse files
committed
Merge #273: proxy-client: tolerate exceptions from remote destroy during cleanup
6de92e1 proxy-client: tolerate exceptions from remote destroy during cleanup (xyzconstant) 90be835 test: regression for ~ProxyClient destroy after peer disconnect (xyzconstant) Pull request description: Fixes #219. Catches exceptions from `Sub::destroy(*this)` in `~ProxyClientBase`'s cleanup lambda so a failed remote destroy call doesn't crash the process. Regression test included. ACKs for top commit: ryanofsky: Code review ACK 6de92e1. Nice fix and test. enirox001: Code review ACK 6de92e1. Tree-SHA512: 1b588670503d339b528d16fd14345243d7c4da0d9714360b43f5c8d92da89387b90f9fab817317603f2ae9c8b1c6eadfb9112fb3cc8f55bf240be13539b41bcf
2 parents 9cec9d6 + 6de92e1 commit 61de697

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

include/mp/proxy-io.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,12 @@ ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client cli
539539
// the remote object, waiting for it to be deleted server side. If the
540540
// capnp interface does not define a destroy method, this will just call
541541
// an empty stub defined in the ProxyClientBase class and do nothing.
542-
Sub::destroy(*this);
542+
// Exceptions are caught and logged rather than propagated because
543+
// ~ProxyClientBase is noexcept and the peer may be gone by the time
544+
// this runs.
545+
if (kj::runCatchingExceptions([&]{ Sub::destroy(*this); }) != nullptr) {
546+
MP_LOG(*m_context.loop, Log::Warning) << "Remote destroy call failed during cleanup. Continuing.";
547+
}
543548

544549
// FIXME: Could just invoke removed addCleanup fn here instead of duplicating code
545550
m_context.loop->sync([&]() {

test/mp/test/test.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,32 @@ KJ_TEST("Calling async IPC method, with server disconnect after cleanup")
434434
}
435435
}
436436

437+
KJ_TEST("Destroying ProxyClient<> with destroy method after peer disconnect")
438+
{
439+
// Regression test for bitcoin-core/libmultiprocess#219 where
440+
// ~ProxyClientBase would call std::terminate if the remote destroy RPC
441+
// failed during teardown.
442+
//
443+
// Save a callback on the server so it holds a ProxyClient<FooCallback>
444+
// pointing back to this side, then disconnect. When the server is torn
445+
// down, the ProxyClient<FooCallback> destructor issues a destroy RPC over
446+
// the now dead connection; without the bugfix the exception escapes the
447+
// noexcept destructor and aborts the process.
448+
449+
TestSetup setup{/*client_owns_connection=*/false};
450+
ProxyClient<messages::FooInterface>* foo = setup.client.get();
451+
foo->initThreadMap();
452+
453+
class Callback : public FooCallback
454+
{
455+
public:
456+
int call(int arg) override { return arg; }
457+
};
458+
459+
foo->saveCallback(std::make_shared<Callback>());
460+
setup.client_disconnect();
461+
}
462+
437463
KJ_TEST("Make simultaneous IPC calls on single remote thread")
438464
{
439465
TestSetup setup;

0 commit comments

Comments
 (0)