From 8a6db55e79ee484bc071fcb52ca30da069231f77 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Mon, 3 May 2021 16:51:42 +0200 Subject: [PATCH] Userland: Add try_* IPC handlers This enables calling auto-generated IPC methods in a way that doesn't crash the client if the peer disconnects. --- Userland/DevTools/IPCCompiler/main.cpp | 53 ++++++++++++++----- Userland/Libraries/LibDesktop/Launcher.cpp | 16 +++--- Userland/Libraries/LibIPC/Message.h | 4 ++ .../LibImageDecoderClient/Client.cpp | 16 +++--- 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/Userland/DevTools/IPCCompiler/main.cpp b/Userland/DevTools/IPCCompiler/main.cpp index 355f031e277..ba5e5fa2930 100644 --- a/Userland/DevTools/IPCCompiler/main.cpp +++ b/Userland/DevTools/IPCCompiler/main.cpp @@ -247,6 +247,7 @@ int main(int argc, char** argv) #pragma once #include #include +#include #include #include #include @@ -496,7 +497,7 @@ public: for (auto& message : endpoint.messages) { auto message_generator = endpoint_generator.fork(); - auto do_implement_proxy = [&](String const& name, Vector const& parameters, bool is_synchronous) { + auto do_implement_proxy = [&](String const& name, Vector const& parameters, bool is_synchronous, bool is_try) { String return_type = "void"; if (is_synchronous) { if (message.outputs.size() == 1) @@ -504,14 +505,23 @@ public: else if (!message.outputs.is_empty()) return_type = message_name(endpoint.name, message.name, true); } + String inner_return_type = return_type; + if (is_try) { + StringBuilder builder; + builder.append("Result<"); + builder.append(return_type); + builder.append(", IPC::ErrorCode>"); + return_type = builder.to_string(); + } message_generator.set("message.name", message.name); message_generator.set("message.pascal_name", pascal_case(message.name)); message_generator.set("message.complex_return_type", return_type); message_generator.set("async_prefix_maybe", is_synchronous ? "" : "async_"); + message_generator.set("try_prefix_maybe", is_try ? "try_" : ""); message_generator.set("handler_name", name); message_generator.append(R"~~~( - @message.complex_return_type@ @async_prefix_maybe@@handler_name@()~~~"); + @message.complex_return_type@ @try_prefix_maybe@@async_prefix_maybe@@handler_name@()~~~"); for (size_t i = 0; i < parameters.size(); ++i) { auto& parameter = parameters[i]; @@ -525,18 +535,21 @@ public: message_generator.append(") {"); - if (is_synchronous) { + if (is_synchronous && !is_try) { if (return_type != "void") { message_generator.append(R"~~~( return )~~~"); if (message.outputs.size() != 1) - message_generator.append(" move(*"); + message_generator.append("move(*"); } else { message_generator.append(R"~~~( )~~~"); } message_generator.append("m_connection.template send_sync("); + } else if (is_try) { + message_generator.append(R"~~~( + auto result = m_connection.template send_sync_but_allow_failure()~~~"); } else { message_generator.append(R"~~~( m_connection.post_message(Messages::@endpoint.name@::@message.pascal_name@ { )~~~"); @@ -554,7 +567,7 @@ public: argument_generator.append(", "); } - if (is_synchronous) { + if (is_synchronous && !is_try) { if (return_type != "void") { message_generator.append(")"); } @@ -566,20 +579,36 @@ public: } else message_generator.append(")"); - message_generator.append(R"~~~(; - } + message_generator.append(";"); + } else if (is_try) { + message_generator.append(R"~~~(); + if (!result) + return IPC::ErrorCode::PeerDisconnected; )~~~"); - + if (inner_return_type != "void") { + message_generator.append(R"~~~( + return move(*result); +)~~~"); + } else { + message_generator.append(R"~~~( + return { }; +)~~~"); + } } else { message_generator.append(R"~~~( }); - } )~~~"); } + + message_generator.append(R"~~~( + } +)~~~"); }; - do_implement_proxy(message.name, message.inputs, message.is_synchronous); - if (message.is_synchronous) - do_implement_proxy(message.name, message.inputs, false); + do_implement_proxy(message.name, message.inputs, message.is_synchronous, false); + if (message.is_synchronous) { + do_implement_proxy(message.name, message.inputs, false, false); + do_implement_proxy(message.name, message.inputs, true, true); + } } endpoint_generator.append(R"~~~( diff --git a/Userland/Libraries/LibDesktop/Launcher.cpp b/Userland/Libraries/LibDesktop/Launcher.cpp index 99e8b902151..d7dcf5743d3 100644 --- a/Userland/Libraries/LibDesktop/Launcher.cpp +++ b/Userland/Libraries/LibDesktop/Launcher.cpp @@ -59,8 +59,8 @@ static LaunchServerConnection& connection() bool Launcher::add_allowed_url(const URL& url) { - auto response = connection().send_sync_but_allow_failure(url); - if (!response) { + auto response_or_error = connection().try_add_allowed_url(url); + if (response_or_error.is_error()) { dbgln("Launcher::add_allowed_url: Failed"); return false; } @@ -69,8 +69,8 @@ bool Launcher::add_allowed_url(const URL& url) bool Launcher::add_allowed_handler_with_any_url(const String& handler) { - auto response = connection().send_sync_but_allow_failure(handler); - if (!response) { + auto response_or_error = connection().try_add_allowed_handler_with_any_url(handler); + if (response_or_error.is_error()) { dbgln("Launcher::add_allowed_handler_with_any_url: Failed"); return false; } @@ -79,8 +79,8 @@ bool Launcher::add_allowed_handler_with_any_url(const String& handler) bool Launcher::add_allowed_handler_with_only_specific_urls(const String& handler, const Vector& urls) { - auto response = connection().send_sync_but_allow_failure(handler, urls); - if (!response) { + auto response_or_error = connection().try_add_allowed_handler_with_only_specific_urls(handler, urls); + if (response_or_error.is_error()) { dbgln("Launcher::add_allowed_handler_with_only_specific_urls: Failed"); return false; } @@ -89,8 +89,8 @@ bool Launcher::add_allowed_handler_with_only_specific_urls(const String& handler bool Launcher::seal_allowlist() { - auto response = connection().send_sync_but_allow_failure(); - if (!response) { + auto response_or_error = connection().try_seal_allowlist(); + if (response_or_error.is_error()) { dbgln("Launcher::seal_allowlist: Failed"); return false; } diff --git a/Userland/Libraries/LibIPC/Message.h b/Userland/Libraries/LibIPC/Message.h index 0cb74bdde1f..48b8cec912b 100644 --- a/Userland/Libraries/LibIPC/Message.h +++ b/Userland/Libraries/LibIPC/Message.h @@ -37,6 +37,10 @@ struct MessageBuffer { Vector> fds; }; +enum class ErrorCode : u32 { + PeerDisconnected +}; + class Message { public: virtual ~Message(); diff --git a/Userland/Libraries/LibImageDecoderClient/Client.cpp b/Userland/Libraries/LibImageDecoderClient/Client.cpp index bf49c7dd7ba..2df07a893a8 100644 --- a/Userland/Libraries/LibImageDecoderClient/Client.cpp +++ b/Userland/Libraries/LibImageDecoderClient/Client.cpp @@ -42,21 +42,23 @@ Optional Client::decode_image(const ByteBuffer& encoded_data) } memcpy(encoded_buffer.data(), encoded_data.data(), encoded_data.size()); - auto response = send_sync_but_allow_failure(move(encoded_buffer)); + auto response_or_error = try_decode_image(move(encoded_buffer)); - if (!response) { + if (response_or_error.is_error()) { dbgln("ImageDecoder died heroically"); return {}; } + auto& response = response_or_error.value(); + DecodedImage image; - image.is_animated = response->is_animated(); - image.loop_count = response->loop_count(); - image.frames.resize(response->bitmaps().size()); + image.is_animated = response.is_animated(); + image.loop_count = response.loop_count(); + image.frames.resize(response.bitmaps().size()); for (size_t i = 0; i < image.frames.size(); ++i) { auto& frame = image.frames[i]; - frame.bitmap = response->bitmaps()[i].bitmap(); - frame.duration = response->durations()[i]; + frame.bitmap = response.bitmaps()[i].bitmap(); + frame.duration = response.durations()[i]; } return image; }