Making these thread local statics makes much more sense for their
purpose. The old approach technically wasn't thread safe (even if it was
never an issue) and this gets rid of a data structure.
We do this by using this new `MessageReference<T>` type to avoid copying
our `YaAudioProcessor::Process` struct and the contained `YaProcessData`
object. This is only part of the work, but this redesign lets us keep
the these objects alive on both the plugin and the host side. On the
plugin side, we'll simply serialize the data from the referred to object
without copying it. On the Wine side, we'll write the data to a
persistent thread local object, and then reassign the
`MessageReference<T>` to point to that object. This lets us serialize
'references', thus avoiding potentially expensive allocations. With
these last few changes alone VST3 plugins are already at the same
performance level as our optimized VST2 plugin groups.
This is kind of equivalent to `std::reference_wrapper`, but with default
initialization support (which is UB, but is required for serialization)
and a forward for `T::Response` as used by your sockets API.
This means that, we're now receiving into an existing `YaProcessData`
object, which should reduce the number of allocations on the Wine side
considerably. Next we should also reuse the `YaProcessDataResponse`
object.
I was wondering why I couldn't find the overload for `UniversalTResult`,
and as it turns out we would always use this variant (which
unsurprisingly was the very first overload added). We should of course
have separate overloads for this.
Bitwig prepends some data when passing an `IBStream*` to the plugin, and
when we do copy it iZotope Rx7 plugins cannot load their state (even if
we also copy over the same seek position). Not copying that preamble
fixes the issue, and it seems like it doesn't break anything.
Hopefully. Checking mapped state apparently wasn't enough to prevent
spurious assertion failures on `is_child_window_or_same()`. Now we'll
just use exceptions instead of assertions so we catch them and ignore
them.
This is probably a better idea than trying to be efficient. If we get
two events in a row, then it can be that the map status has changed
between the two events.
VST3 plugins could freeze in REAPER when the plugin sends
`IComponentHandler::performEdit()` followed by
`IPlugFrame::resizeView()` when REAPER simultaneously tries to call
`*::getState()`. Here `*::getState()` gets called from the GUI thread,
while `IPlugFrame::resizeView()` has to be handled on REAPER's GUI
thread, resulting in a deadlock unless we use the plugin-side mutual
recursion mechanism.
I've seen this cause issues with PSPaudioware InfiniStrip.
The VST3 version of Voxengo TEOTE would deadlock in Ardour when Ardour
calls `IEditController::setState()`, the plugin calls
`IUnitHandler::notifyProgramListChange()` in response, and then when
Ardour calls `IUnitInfo::getProgramName()` while handling that callback.
All of these functions have to be called from the same thread in Voxengo
plugins.
Apparently the destructor for C++17/20 conditional initializers are only
run after the else branch, so this would keep the mutex locked while
executing `do_call()`.
For individually hosted plugins this will behave the same. For plugin
groups it would have been nice if we could shut down individual plugins,
but dangling threads are apparently a real issue. This should be
equivalent in all use cases.
This also reverts commit bda9a0b75f.
We would never try to shut the group host down if nothing ever tried to
connect to it. This could happen when the native host gets killed after
initializing the yabridge plugin but before it gets the chance to
request the group host process to host a plugin.
This will let us more or less gracefully handle failing host callbacks
during initialization. We cannot catch this from anywhere else since
this these functions get called from unmanaged code.