From f50d04ce0415e9e93db1865b433c0481d18661ff Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 23 May 2020 13:54:40 +0200 Subject: [PATCH] Defer group host shutdown This allows for a significant speedup in plugin scanning time for plugin groups, since starting Wine processes takes up pretty much the entirety of the time spent scanning plugins. --- src/wine-host/bridges/group.cpp | 30 +++++++++++++++++++++++------- src/wine-host/bridges/group.h | 13 ++++++++++++- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/wine-host/bridges/group.cpp b/src/wine-host/bridges/group.cpp index 5510d36a..6ad15b32 100644 --- a/src/wine-host/bridges/group.cpp +++ b/src/wine-host/bridges/group.cpp @@ -96,13 +96,16 @@ GroupBridge::GroupBridge(boost::filesystem::path group_socket_path) stderr_redirect(io_context, STDERR_FILENO), group_socket_endpoint(group_socket_path.string()), group_socket_acceptor( - create_acceptor_if_inactive(io_context, group_socket_endpoint)) { + create_acceptor_if_inactive(io_context, group_socket_endpoint)), + shutdown_timer(io_context) { // Write this process's original STDOUT and STDERR streams to the logger async_log_pipe_lines(stdout_redirect.pipe, stdout_buffer, "[STDOUT] "); async_log_pipe_lines(stderr_redirect.pipe, stderr_buffer, "[STDERR] "); } void GroupBridge::handle_host_plugin(const GroupRequest request) { + using namespace std::literals::chrono_literals; + // At this point the `active_plugins` map will already contain a copy of // `parameters` along with this thread's handle // The initialization process for a plugin is identical to that in @@ -124,14 +127,27 @@ void GroupBridge::handle_host_plugin(const GroupRequest request) { // After the plugin has exited (either after `effClose()` or because it // failed to initialize), we'll remove this thread's plugin from the active - // plugins. If no active plugins remain, then we'll terminate + // plugins. If no active plugins remain, then we'll terminate this process. std::lock_guard lock(active_plugins_mutex); - active_plugins.erase(request); - if (active_plugins.size() == 0) { - logger.log("All plugins have exited, shutting down the group process"); - io_context.stop(); - } + + // Defer shutting down the process to allow for fast plugin scanning by + // allowing plugins to reuse the same group host process + shutdown_timer.expires_after(2s); + shutdown_timer.async_wait([&](const boost::system::error_code& error) { + // A previous timer gets canceled automatically when another plugin + // exits + if (error.failed()) { + return; + } + + std::lock_guard lock(active_plugins_mutex); + if (active_plugins.size() == 0) { + logger.log( + "All plugins have exited, shutting down the group process"); + io_context.stop(); + } + }); } void GroupBridge::handle_incoming_connections() { diff --git a/src/wine-host/bridges/group.h b/src/wine-host/bridges/group.h index 93039bef..fa2909f2 100644 --- a/src/wine-host/bridges/group.h +++ b/src/wine-host/bridges/group.h @@ -126,7 +126,9 @@ class GroupBridge { * * Once the plugin has exited, this thread will then remove itself from the * `active_plugins` map. If this causes the vector to become empty, we will - * terminate this process. + * terminate this process. This check will be delayed by a few seconds to + * prevent having to constantly restart the group process during plugin + * scanning. * * @param request Information about the plugin to launch, i.e. the path to * the plugin and the path of the socket endpoint that will be used for @@ -216,4 +218,13 @@ class GroupBridge { * plugin is being spawned. */ std::mutex active_plugins_mutex; + + /** + * A timer to defer shutting down the process, allowing for fast plugin + * scanning without having to start a new group host process for each + * plugin. + * + * @see handle_host_plugin + */ + boost::asio::steady_timer shutdown_timer; };