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
This commit is contained in:
Robin Gareus 2023-12-10 22:08:59 +01:00
parent f191d8ba94
commit 4402e2a3a8
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
3 changed files with 14 additions and 3 deletions

View File

@ -40,6 +40,10 @@ public:
Steinberg::IPluginFactory* factory ();
bool has_symbol (const char* name) const {
return NULL != fn_ptr (name);
}
protected:
void release_factory ();

View File

@ -358,6 +358,8 @@ private:
/* work around UADx plugin crash */
bool _no_kMono;
/* work around yabridge threading */
bool _restart_component_is_synced;
};
} // namespace Steinberg

View File

@ -1173,6 +1173,7 @@ VST3PI::VST3PI (std::shared_ptr<ARDOUR::VST3PluginModule> 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<ARDOUR::VST3PluginModule> 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.