From 4402e2a3a86ca6794be35c41b55a54eb84bc2efa Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 10 Dec 2023 22:08:59 +0100 Subject: [PATCH] Add workaround for yabridge threading * yabridge runs the plugin's process function in a dedicated bridged thread. Ardour's process thread is not (it just waits) * When a plugin calls `restartComponent` from the realtime thread. yabridge uses a host notification thread to perform the callback. Unlike other VST3 implementations that use a notification thread (eg. JUCE), yabridge blocks and waits for the notification to complete before the realtime thread can continue. This leads to a deadlock. However, we know that yabridge always synchronizes the callback and concurrent calls are prevented by yabridge's design. https://github.com/robbert-vdh/yabridge/issues/266 --- libs/ardour/ardour/vst3_module.h | 4 ++++ libs/ardour/ardour/vst3_plugin.h | 2 ++ libs/ardour/vst3_plugin.cc | 11 ++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/libs/ardour/ardour/vst3_module.h b/libs/ardour/ardour/vst3_module.h index 4610726398..f3eb18fcde 100644 --- a/libs/ardour/ardour/vst3_module.h +++ b/libs/ardour/ardour/vst3_module.h @@ -40,6 +40,10 @@ public: Steinberg::IPluginFactory* factory (); + bool has_symbol (const char* name) const { + return NULL != fn_ptr (name); + } + protected: void release_factory (); diff --git a/libs/ardour/ardour/vst3_plugin.h b/libs/ardour/ardour/vst3_plugin.h index bb4cdc1d7c..96269fd3e7 100644 --- a/libs/ardour/ardour/vst3_plugin.h +++ b/libs/ardour/ardour/vst3_plugin.h @@ -358,6 +358,8 @@ private: /* work around UADx plugin crash */ bool _no_kMono; + /* work around yabridge threading */ + bool _restart_component_is_synced; }; } // namespace Steinberg diff --git a/libs/ardour/vst3_plugin.cc b/libs/ardour/vst3_plugin.cc index ca431257d2..ce3627dcfb 100644 --- a/libs/ardour/vst3_plugin.cc +++ b/libs/ardour/vst3_plugin.cc @@ -1173,6 +1173,7 @@ VST3PI::VST3PI (std::shared_ptr m, std::string unique_ , _block_rpc (0) , _rpc_queue (RouteProcessorChange::NoProcessorChange, false) , _no_kMono (false) + , _restart_component_is_synced (false) { using namespace std; IPluginFactory* factory = m->factory (); @@ -1195,6 +1196,10 @@ VST3PI::VST3PI (std::shared_ptr m, std::string unique_ } } +#if !(defined PLATFORM_WINDOWS || defined __APPLE__) /* Linux only */ + _restart_component_is_synced = m->has_symbol ("yabridge_version"); +#endif + #ifndef NDEBUG if (DEBUG_ENABLED (DEBUG::VST3Config)) { char fuid[33]; @@ -1505,7 +1510,7 @@ VST3PI::restartComponent (int32 flags) if (flags & Vst::kReloadComponent) { Glib::Threads::Mutex::Lock pl (_process_lock, Glib::Threads::NOT_LOCK); - if (!AudioEngine::instance ()->in_process_thread () && !_is_loading_state) { + if (!AudioEngine::instance ()->in_process_thread () && !_is_loading_state && !_restart_component_is_synced) { pl.acquire (); } else { assert (0); // a plugin should not call this while processing @@ -1523,7 +1528,7 @@ VST3PI::restartComponent (int32 flags) } if (flags & Vst::kParamValuesChanged) { Glib::Threads::Mutex::Lock pl (_process_lock, Glib::Threads::NOT_LOCK); - if (!AudioEngine::instance ()->in_process_thread () && !_is_loading_state) { + if (!AudioEngine::instance ()->in_process_thread () && !_is_loading_state && !_restart_component_is_synced) { pl.acquire (); } update_shadow_data (); @@ -1538,7 +1543,7 @@ VST3PI::restartComponent (int32 flags) * changes are automatically picked up. */ Glib::Threads::Mutex::Lock pl (_process_lock, Glib::Threads::NOT_LOCK); - if (!AudioEngine::instance ()->in_process_thread () && !_is_loading_state) { + if (!AudioEngine::instance ()->in_process_thread () && !_is_loading_state && !_restart_component_is_synced) { /* Some plugins (e.g BlendEQ) call this from the process, * IPlugProcessor::ProcessBuffers. In that case taking the * _process_lock would deadlock.