From 13d27987f49914be1ca7672c4da0818872eddfb0 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 2 Feb 2023 14:35:58 +0000 Subject: [PATCH] VST3 Client: Enforce that process and setActive are not called simultaneously in FL Studio Opening some JUCE VST3s in FL Studio's patcher can cause crashes because of data races on objects accessed from setActive() and process(). According to the VST3 specification, these functions should never be called simultaneously, so I think this is a bug in FL Studio. --- .../VST3/juce_VST3_Wrapper.cpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 7e0d400e4a..7a1746fe97 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -2551,6 +2551,8 @@ public: //============================================================================== tresult PLUGIN_API setActive (TBool state) override { + const FLStudioDIYSpecificationEnforcementLock lock (flStudioDIYSpecificationEnforcementMutex); + const auto willBeActive = (state != 0); active = false; @@ -3140,6 +3142,8 @@ public: Steinberg::int32 index, TBool state) override { + const FLStudioDIYSpecificationEnforcementLock lock (flStudioDIYSpecificationEnforcementMutex); + // The host is misbehaving! The plugin must be deactivated before setting new arrangements. jassert (! active); @@ -3274,6 +3278,8 @@ public: tresult PLUGIN_API setBusArrangements (Vst::SpeakerArrangement* inputs, Steinberg::int32 numIns, Vst::SpeakerArrangement* outputs, Steinberg::int32 numOuts) override { + const FLStudioDIYSpecificationEnforcementLock lock (flStudioDIYSpecificationEnforcementMutex); + if (active) { // The host is misbehaving! The plugin must be deactivated before setting new arrangements. @@ -3501,6 +3507,8 @@ public: tresult PLUGIN_API process (Vst::ProcessData& data) override { + const FLStudioDIYSpecificationEnforcementLock lock (flStudioDIYSpecificationEnforcementMutex); + if (pluginInstance == nullptr) return kResultFalse; @@ -3573,6 +3581,24 @@ public: } private: + /* FL's Patcher implements the VST3 specification incorrectly, calls process() before/during + setActive(). + */ + class [[nodiscard]] FLStudioDIYSpecificationEnforcementLock + { + public: + explicit FLStudioDIYSpecificationEnforcementLock (CriticalSection& mutex) + { + static const auto lockRequired = PluginHostType().isFruityLoops(); + + if (lockRequired) + lock.emplace (mutex); + } + + private: + std::optional lock; + }; + InterfaceResultWithDeferredAddRef queryInterfaceInternal (const TUID targetIID) { const auto result = testForMultiple (*this, @@ -3790,6 +3816,7 @@ private: #endif static const char* kJucePrivateDataIdentifier; + CriticalSection flStudioDIYSpecificationEnforcementMutex; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3Component) };