Fix mutual recursion with latency in Ardour/Mixbus

This would cause Ardour and Mixbus to freeze when inserting a latency
introducing (JUCE based) VST3 plugin. As mentioned in #98.
This commit is contained in:
Robbert van der Helm
2021-04-29 03:09:42 +02:00
parent 22f94dd22f
commit 8b168b310c
4 changed files with 49 additions and 4 deletions
+3
View File
@@ -61,6 +61,9 @@ Versioning](https://semver.org/spec/v2.0.0.html).
plugins. We now explicitly reparent the window to back the root window first
before deferring the window closing. This should work around the issue, while
still keeping editor closing nice and snappy.
- Prevented latency introducing plugins from causing **Ardour** and **Mixbus**
to freeze. This for example prevents _Neural DSP Darkglass_ from freezing when
used under those DAWs.
- _PSPaudioware InifniStrip_ would fail to initialize because the plugin expects
the host to always be using Microsoft COM and it won't initialize it by
itself. InfiniStrip loads as expected now.
@@ -61,8 +61,9 @@ Vst3ComponentHandlerProxyImpl::endEdit(Steinberg::Vst::ParamID id) {
tresult PLUGIN_API
Vst3ComponentHandlerProxyImpl::restartComponent(int32 flags) {
return bridge.send_message(YaComponentHandler::RestartComponent{
.owner_instance_id = owner_instance_id(), .flags = flags});
return bridge.send_mutually_recursive_message(
YaComponentHandler::RestartComponent{
.owner_instance_id = owner_instance_id(), .flags = flags});
}
tresult PLUGIN_API Vst3ComponentHandlerProxyImpl::setDirty(TBool state) {
+16 -2
View File
@@ -1300,8 +1300,22 @@ size_t Vst3Bridge::register_object_instance(
},
[&](const YaComponent::SetActive& request)
-> YaComponent::SetActive::Response {
return object_instances[request.instance_id]
.component->setActive(request.state);
// NOTE: Ardour/Mixbus will immediately call this
// function in response to a latency change
// announced through
// `IComponentHandler::restartComponent()`. We
// need to make sure that these two functions are
// handled from the same thread to prevent
// deadlocks caused by mutually recursive function
// calls.
// TODO: Check if this causes any issues when activating
// plugins while simultaneously resizing another
// instance of the same plugin
return do_mutual_recursion_or_handle_in_main_context<
tresult>([&]() {
return object_instances[request.instance_id]
.component->setActive(request.state);
});
},
[&](const YaPrefetchableSupport::GetPrefetchableSupport&
request)
+27
View File
@@ -256,6 +256,11 @@ class Vst3Bridge : public HostBridge {
* but in this sequence that thread is being blocked by a call to
* `IPlugFrame::resizeView()`.
*
* We also need to use this for when a plugin calls
* `IComponentHandler::restartComponent()` to change the latency, and when
* the host then calls `IAudioProcessor::setActive()` in response to that to
* restart the plugin. Otherwise this will lead to an infinite loop.
*
* The hacky solution here is to send the message from another thread, and
* to then allow this thread to execute other functions submitted to an IO
* context.
@@ -346,6 +351,28 @@ class Vst3Bridge : public HostBridge {
return do_call_response.get();
}
/**
* The same as the above function, but we'll just execute the function on
* this thread when the mutual recursion context is not active.
*
* @see Vst3Bridge::do_mutual_recursion_or_handle_in_main_context
*/
template <typename T, typename F>
T do_mutual_recursion(F f) {
std::packaged_task<T()> do_call(std::move(f));
std::future<T> do_call_response = do_call.get_future();
if (std::lock_guard lock(mutual_recursion_contexts_mutex);
!mutual_recursion_contexts.empty()) {
boost::asio::dispatch(*mutual_recursion_contexts.back(),
std::move(do_call));
} else {
do_call();
}
return do_call_response.get();
}
/**
* Register a context with with `context_menu`'s ID and owner in
* `object_instances`. This will be called during the constructor of