From 096e36dfdc6a2c7ac41c4a6cf98efb856e2ff348 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 23 Mar 2020 23:41:00 +0100 Subject: [PATCH] Handle all dispatch calls from the main thread Some plugins apparently assume that all events (or at least some specific ones) are sent from the thread it was created on and will segfault otherwise. The is the case for Melda plugins. --- src/wine-host/plugin-bridge.cpp | 43 +++++++++++++++++---------------- src/wine-host/plugin-bridge.h | 12 ++++----- src/wine-host/vst-host.cpp | 4 +-- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index e7941d49..015e5761 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -118,19 +118,6 @@ PluginBridge::PluginBridge(std::string plugin_dll_path, current_bridge_isntance = nullptr; plugin->ptr1 = this; - // For our communication we use simple threads and blocking operations - // instead of asynchronous IO since communication has to be handled in - // lockstep anyway - dispatch_handler = std::thread([&]() { - using namespace std::placeholders; - - while (true) { - passthrough_event(host_vst_dispatch, std::nullopt, plugin, - std::bind(&PluginBridge::dispatch_wrapper, this, - _1, _2, _3, _4, _5, _6)); - } - }); - parameters_handler = std::thread([&]() { while (true) { // Both `getParameter` and `setParameter` functions are passed @@ -189,6 +176,26 @@ PluginBridge::PluginBridge(std::string plugin_dll_path, << std::endl; } +void PluginBridge::handle_dispatch() { + using namespace std::placeholders; + + // For our communication we use simple threads and blocking operations + // instead of asynchronous IO since communication has to be handled in + // lockstep anyway + try { + while (true) { + passthrough_event(host_vst_dispatch, std::nullopt, plugin, + std::bind(&PluginBridge::dispatch_wrapper, this, + _1, _2, _3, _4, _5, _6)); + } + } catch (const boost::system::system_error&) { + // This happens when the sockets got closed because the plugin is being + // shut down. In that case we can just let the whole host terminate. + parameters_handler.detach(); + process_replacing_handler.detach(); + } +} + intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin, int opcode, int index, @@ -199,9 +206,9 @@ intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin, // the X11 window handle passed by the host switch (opcode) { case effEditIdle: - // Because of the way the Win32 APi works we have to process events + // Because of the way the Win32 API works we have to process events // on the same thread the window was created, and that thread is the - // `dispatch_handler` thread + // thread that's handling dispatcher calls editor.handle_events(); return plugin->dispatcher(plugin, opcode, index, value, data, @@ -261,12 +268,6 @@ intptr_t PluginBridge::dispatch_wrapper(AEffect* plugin, } } -void PluginBridge::wait() { - dispatch_handler.join(); - parameters_handler.join(); - process_replacing_handler.join(); -} - class HostCallbackDataConverter : DefaultDataConverter { public: HostCallbackDataConverter(AEffect* plugin, diff --git a/src/wine-host/plugin-bridge.h b/src/wine-host/plugin-bridge.h index 3400cbfb..bd1d598a 100644 --- a/src/wine-host/plugin-bridge.h +++ b/src/wine-host/plugin-bridge.h @@ -55,9 +55,13 @@ class PluginBridge { PluginBridge(std::string plugin_dll_path, std::string socket_endpoint_path); /** - * Block the main thread until the plugin shuts down. + * Handle events on the main thread until the plugin quits. This can't be + * done on another thread since some plugins (e.g. Melda) expect certain + * (but for some reason not all) events to be passed from the same thread it + * was initiated from. This is then also the same thread that should handle + * Win32 GUI events. */ - void wait(); + void handle_dispatch(); intptr_t host_callback(AEffect*, int, int, intptr_t, void*, float); @@ -115,10 +119,6 @@ class PluginBridge { */ boost::asio::local::stream_protocol::socket vst_host_aeffect; - /** - * The thread that handles dispatch events from the host. - */ - std::thread dispatch_handler; /** * The thread that responds to `getParameter` and `setParameter` requests. */ diff --git a/src/wine-host/vst-host.cpp b/src/wine-host/vst-host.cpp index 83f2649e..f782fa6d 100644 --- a/src/wine-host/vst-host.cpp +++ b/src/wine-host/vst-host.cpp @@ -35,8 +35,8 @@ int main(int argc, char* argv[]) { try { PluginBridge bridge(plugin_dll_path, socket_endpoint_path); - // Block the main thread until the plugin shuts down - bridge.wait(); + // Blocks the main thread until the plugin shuts down + bridge.handle_dispatch(); } catch (const std::runtime_error& error) { std::cerr << "Error while initializing Wine VST host:" << std::endl; std::cerr << error.what() << std::endl;