Skip to content

Commit f15ef6c

Browse files
committed
Improve IPC client disconnected exceptions
Improve clientInvoke exceptions so IPC clients can more reliably detect when they are calling a remote method after the connection is closed. Before this change different exceptions were thrown, which made this condition difficult to detect and handle.
1 parent 4a9a387 commit f15ef6c

File tree

2 files changed

+21
-12
lines changed

2 files changed

+21
-12
lines changed

include/mp/proxy-types.h

+17-6
Original file line numberDiff line numberDiff line change
@@ -582,9 +582,6 @@ void serverDestroy(Server& server)
582582
template <typename ProxyClient, typename GetRequest, typename... FieldObjs>
583583
void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, FieldObjs&&... fields)
584584
{
585-
if (!proxy_client.m_context.connection) {
586-
throw std::logic_error("clientInvoke call made after disconnect");
587-
}
588585
if (!g_thread_context.waiter) {
589586
assert(g_thread_context.thread_name.empty());
590587
g_thread_context.thread_name = ThreadName(proxy_client.m_context.loop->m_exe_name);
@@ -606,7 +603,16 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
606603
std::exception_ptr exception;
607604
std::string kj_exception;
608605
bool done = false;
606+
const char* disconnected = nullptr;
609607
proxy_client.m_context.loop->sync([&]() {
608+
if (!proxy_client.m_context.connection) {
609+
const std::unique_lock<std::mutex> lock(invoke_context.thread_context.waiter->m_mutex);
610+
done = true;
611+
disconnected = "IPC client method called after disconnect.";
612+
invoke_context.thread_context.waiter->m_cv.notify_all();
613+
return;
614+
}
615+
610616
auto request = (proxy_client.m_client.*get_request)(nullptr);
611617
using Request = CapRequestTraits<decltype(request)>;
612618
using FieldList = typename ProxyClientMethodTraits<typename Request::Params>::Fields;
@@ -631,9 +637,13 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
631637
invoke_context.thread_context.waiter->m_cv.notify_all();
632638
},
633639
[&](const ::kj::Exception& e) {
634-
kj_exception = kj::str("kj::Exception: ", e).cStr();
635-
proxy_client.m_context.loop->logPlain()
636-
<< "{" << invoke_context.thread_context.thread_name << "} IPC client exception " << kj_exception;
640+
if (e.getType() == ::kj::Exception::Type::DISCONNECTED) {
641+
disconnected = "IPC client method call interrupted by disconnect.";
642+
} else {
643+
kj_exception = kj::str("kj::Exception: ", e).cStr();
644+
proxy_client.m_context.loop->logPlain()
645+
<< "{" << invoke_context.thread_context.thread_name << "} IPC client exception " << kj_exception;
646+
}
637647
const std::unique_lock<std::mutex> lock(invoke_context.thread_context.waiter->m_mutex);
638648
done = true;
639649
invoke_context.thread_context.waiter->m_cv.notify_all();
@@ -644,6 +654,7 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
644654
invoke_context.thread_context.waiter->wait(lock, [&done]() { return done; });
645655
if (exception) std::rethrow_exception(exception);
646656
if (!kj_exception.empty()) proxy_client.m_context.loop->raise() << kj_exception;
657+
if (disconnected) proxy_client.m_context.loop->raise() << disconnected;
647658
}
648659

649660
//! Invoke callable `fn()` that may return void. If it does return void, replace

test/mp/test/test.cpp

+4-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace test {
3535
* object destroys the client Connection object. Normally it makes sense for
3636
* this to be true to simplify shutdown and avoid needing to call
3737
* client_disconnect manually, but false allows testing more ProxyClient
38-
* behavior and the "clientInvoke call made after disconnect" code path.
38+
* behavior and the "IPC client method called after disconnect" code path.
3939
*/
4040
class TestSetup
4141
{
@@ -189,8 +189,8 @@ KJ_TEST("Call IPC method after client connection is closed")
189189
bool disconnected{false};
190190
try {
191191
foo->add(1, 2);
192-
} catch (const std::logic_error& e) {
193-
KJ_EXPECT(std::string_view{e.what()} == "clientInvoke call made after disconnect");
192+
} catch (const std::runtime_error& e) {
193+
KJ_EXPECT(std::string_view{e.what()} == "IPC client method called after disconnect.");
194194
disconnected = true;
195195
}
196196
KJ_EXPECT(disconnected);
@@ -207,9 +207,7 @@ KJ_TEST("Calling IPC method after server connection is closed")
207207
try {
208208
foo->add(1, 2);
209209
} catch (const std::runtime_error& e) {
210-
std::string_view error{e.what()};
211-
KJ_EXPECT(error.starts_with("kj::Exception: "));
212-
KJ_EXPECT(error.find("disconnected: Peer disconnected.") != std::string_view::npos);
210+
KJ_EXPECT(std::string_view{e.what()} == "IPC client method call interrupted by disconnect.");
213211
disconnected = true;
214212
}
215213
KJ_EXPECT(disconnected);

0 commit comments

Comments
 (0)