From ff76e482f2a6a6703403bf684d3a0c675ff2ed28 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 20 Jul 2021 16:22:27 +0200 Subject: [PATCH] Inhibit the event loop during VST3 editor init This should in theory prevent Nimble Kick from triggering a stack overflow when the event loop timer procs before `IPlugView::attached()` gets called and the plugin hasn't been registered yet. I haven't seen any other VST3 plugins trigger a race condition here. --- CHANGELOG.md | 2 ++ src/wine-host/bridges/vst3.cpp | 37 +++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e97dcbf..de0593b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ Versioning](https://semver.org/spec/v2.0.0.html). like FrozenPlain **Obelisk** would result in a crash. - Fixed a regression from yabridge 3.4.0 where JUCE-based VST3 plugins might cause **Ardour** or **Mixbus** to freeze in very specific circumstances. +- Worked around a race condition in _Nimble Kick_, which would trigger a stack + overflow when loading the plugin if it wasn't already activated. - When the window manager somehow steals yabridge's window away from the host, yabridge will now try to steal it back and reparent it to the host's window again. This very rarely happened with some window managers, like XFWM, and diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 15e16873..cecc7ffb 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -499,6 +499,16 @@ void Vst3Bridge::run() { if (plug_view) { instance.plug_view_instance.emplace(plug_view); + + // HACK: Nimble Kick (and perhaps other plugins, but + // I haven't heard any reports of this being + // an issue coming from other plugins) starts + // a timer in `IEditController::createView()` + // that relies on data that is only + // initialized once `IPlugView::attached()` + // has been called. So until that point, we'll + // prevent the event loop from running. + instance.is_initialized = false; } else { instance.plug_view_instance.reset(); } @@ -716,6 +726,9 @@ void Vst3Bridge::run() { }, [&](const YaPlugView::Attached& request) -> YaPlugView::Attached::Response { + Vst3PluginInstance& instance = + object_instances.at(request.owner_instance_id); + const std::string type = request.type == Steinberg::kPlatformTypeX11EmbedWindowID ? Steinberg::kPlatformTypeHWND @@ -729,23 +742,27 @@ void Vst3Bridge::run() { // be done in the main UI thread return main_context .run_in_context([&]() -> tresult { - Editor& editor_instance = - object_instances.at(request.owner_instance_id) - .editor.emplace(main_context, config, - generic_logger, x11_handle); + Editor& editor_instance = instance.editor.emplace( + main_context, config, generic_logger, x11_handle); const tresult result = - object_instances.at(request.owner_instance_id) - .plug_view_instance->plug_view->attached( - editor_instance.get_win32_handle(), - type.c_str()); + instance.plug_view_instance->plug_view->attached( + editor_instance.get_win32_handle(), + type.c_str()); // Get rid of the editor again if the plugin didn't // embed itself in it if (result != Steinberg::kResultOk) { - object_instances.at(request.owner_instance_id) - .editor.reset(); + instance.editor.reset(); } + // HACK: See the comment in our handling of + // `IEditController::createView()`. We'll reset + // this regardless of whether or not this request + // succeeded or else JUCE plugins without editors + // might cause us to get stuck in a situation + // where the event loop gets blocked indefinitely. + instance.is_initialized = true; + return result; }) .get();