From 5f7fb2e2c3c75e2bd1f7fca8d239aec35a806889 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 15 Jul 2021 15:45:15 +0200 Subject: [PATCH] Work around thread safety issue in Melda plugins This is super difficult to trigger on purpose, but I did run into it at least once just now so it seems like a good idea to at least make sure that this doesn't happen. --- CHANGELOG.md | 3 +++ src/wine-host/bridges/vst3.cpp | 28 ++++++++++++++++++++++------ src/wine-host/bridges/vst3.h | 13 +++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eae26833..274c3b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,9 @@ Versioning](https://semver.org/spec/v2.0.0.html). tries to use REAPER's [host function API](https://www.reaper.fm/sdk/vst/vst_ext.php#vst_host). This currently isn't supported by yabridge. We now explicitly ignore these requests. +- Worked around a rare thread safety issue in _MeldaProduction_ VST3 plugins + where the plugin would deadlock when the host asks for the editor's size while + plugin is also being initialized form the audio thread. - Fixed JUCE VST3 plugins like Tokyo Dawn Records' _SlickEQ M_ causing the host to freeze when they send a parameter change from the audio thread using the wrong VST3 API while the plugin is also trying to resize the window from the diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 0f4a5149..7a634426 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -800,13 +800,20 @@ void Vst3Bridge::run() { .get(); }, [&](YaPlugView::GetSize& request) -> YaPlugView::GetSize::Response { + Vst3PluginInstance& instance = + object_instances.at(request.owner_instance_id); + // Melda plugins will refuse to open dialogs of this function is - // not run from the GUI thread + // not run from the GUI thread. Oh and they also deadlock if + // audio processing gets initialized at the same time as this + // function, not sure why. + std::lock_guard lock(instance.get_size_mutex); + Steinberg::ViewRect size{}; const tresult result = do_mutual_recursion_on_gui_thread([&]() -> tresult { - return object_instances.at(request.owner_instance_id) - .plug_view_instance->plug_view->getSize(&size); + return instance.plug_view_instance->plug_view->getSize( + &size); }); return YaPlugView::GetSizeResponse{.result = result, @@ -1419,9 +1426,18 @@ size_t Vst3Bridge::register_object_instance( }, [&](const YaAudioProcessor::SetProcessing& request) -> YaAudioProcessor::SetProcessing::Response { - return object_instances.at(request.instance_id) - .interfaces.audio_processor->setProcessing( - request.state); + Vst3PluginInstance& instance = + object_instances.at(request.instance_id); + + // HACK: MeldaProduction plugins for some reason cannot + // handle it if this function is called from the + // audio thread while at the same time + // `IPlugView::getSize()` is being called from the + // GUI thread + std::lock_guard lock(instance.get_size_mutex); + + return instance.interfaces.audio_processor + ->setProcessing(request.state); }, [&](MessageReference& request_ref) diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index 414de229..5c5a4b71 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -216,6 +216,19 @@ struct Vst3PluginInstance { */ std::optional plug_view_instance; + /** + * Used to make sure that `IPlugView::getSize()` can never be called at the + * same time as `IAudioProcessor::setProcessing()`. + * + * HACK: This really shouldn't be needed, but MeldaProduction plugins seem + * to deadlock when this happens. It's pretty tricky to reproduce the + * timing for making this happen (since opening the GUI also needs to + * be delayed slightly, like when opening a plugin from Bitwig's popup + * browser), but it seems like a good idea to make sure that this + * doesn't cause any freezes. + */ + std::mutex get_size_mutex; + /** * This contains smart pointers to all VST3 plugin interfaces that can be * casted from `object`.