Thanks for the information. I was using Reaper on macOS for the tests.
Yes, I’m having two threads. The Main Thread, which loads the preset and was triggered by the UI and the audio processor that also sends parameter changes from the Audio-Thread.
It is LockedListeners
where the deadlock happens. Two different threads call setValueNotifyingHost()
and I’m having a few listeners attached.
We have two threads that are locked:
__psynch_mutexwait 0x00007fff72409062
_pthread_mutex_firstfit_lock_wait 0x00007fff724c7917
_pthread_mutex_firstfit_lock_slow 0x00007fff724c5937
juce::CriticalSection::enter() const juce_posix_SharedCode.h:39
juce::GenericScopedLock::GenericScopedLock(const juce::CriticalSection &) juce_ScopedLock.h:67
juce::GenericScopedLock::GenericScopedLock(const juce::CriticalSection &) juce_ScopedLock.h:67
juce::AudioProcessorValueTreeState::ParameterAdapter::LockedListeners::call<…>(<lambda> &&) juce_AudioProcessorValueTreeState.cpp:194
juce::AudioProcessorValueTreeState::ParameterAdapter::parameterValueChanged(int, float) juce_AudioProcessorValueTreeState.cpp:165
<lambda>::operator()() const juce_AudioProcessorValueTreeState.cpp:93
decltype(std::__1::forward<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(fp)()) std::__1::__invoke<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&) type_traits:3545
std::__invoke_void_return_wrapper::__call<…>(<lambda> &) __functional_base:348
std::__function::__alloc_func::operator()() functional:1546
std::__function::__func::operator()() functional:1720
std::__function::__value_func::operator()() const functional:1873
std::function::operator()() const functional:2548
juce::AudioProcessorValueTreeState::Parameter::valueChanged(float) juce_AudioProcessorValueTreeState.cpp:75
juce::AudioParameterFloat::setValue(float) juce_AudioParameterFloat.cpp:87
juce::AudioProcessorParameter::setValueNotifyingHost(float) juce_AudioProcessor.cpp:1508
DelayEngine::parameterChanged(const juce::String &, float) DelayEngine.h:253
<lambda>::operator()(juce::AudioProcessorValueTreeState::Listener &) const juce_AudioProcessorValueTreeState.cpp:165
juce::ListenerList::call<…>(<lambda> &&) juce_ListenerList.h:124
juce::AudioProcessorValueTreeState::ParameterAdapter::LockedListeners::call<…>(<lambda> &&) juce_AudioProcessorValueTreeState.cpp:195
juce::AudioProcessorValueTreeState::ParameterAdapter::parameterValueChanged(int, float) juce_AudioProcessorValueTreeState.cpp:165
<lambda>::operator()() const juce_AudioProcessorValueTreeState.cpp:93
decltype(std::__1::forward<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(fp)()) std::__1::__invoke<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&) type_traits:3545
std::__invoke_void_return_wrapper::__call<…>(<lambda> &) __functional_base:348
std::__function::__alloc_func::operator()() functional:1546
std::__function::__func::operator()() functional:1720
std::__function::__value_func::operator()() const functional:1873
std::function::operator()() const functional:2548
juce::AudioProcessorValueTreeState::Parameter::valueChanged(float) juce_AudioProcessorValueTreeState.cpp:75
juce::AudioParameterFloat::setValue(float) juce_AudioParameterFloat.cpp:87
juce::AudioProcessorParameter::setValueNotifyingHost(float) juce_AudioProcessor.cpp:1508
juce::setValueAndNotifyIfChanged(juce::AudioProcessorParameter &, float) juce_VST3_Wrapper.cpp:633
juce::JuceVST3Component::processParameterChanges(Steinberg::Vst::IParameterChanges &) juce_VST3_Wrapper.cpp:3097
juce::JuceVST3Component::process(Steinberg::Vst::ProcessData &) juce_VST3_Wrapper.cpp:3150
VST3_ProcessReplacing(AEffect *, float **, float **, int) 0x000000010c0e4ac1
__psynch_mutexwait 0x00007fff72409062
_pthread_mutex_firstfit_lock_wait 0x00007fff724c7917
_pthread_mutex_firstfit_lock_slow 0x00007fff724c5937
juce::CriticalSection::enter() const juce_posix_SharedCode.h:39
juce::GenericScopedLock::GenericScopedLock(const juce::CriticalSection &) juce_ScopedLock.h:67
juce::GenericScopedLock::GenericScopedLock(const juce::CriticalSection &) juce_ScopedLock.h:67
juce::AudioProcessorValueTreeState::ParameterAdapter::LockedListeners::call<…>(<lambda> &&) juce_AudioProcessorValueTreeState.cpp:194
juce::AudioProcessorValueTreeState::ParameterAdapter::parameterValueChanged(int, float) juce_AudioProcessorValueTreeState.cpp:165
<lambda>::operator()() const juce_AudioProcessorValueTreeState.cpp:93
decltype(std::__1::forward<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(fp)()) std::__1::__invoke<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&) type_traits:3545
std::__invoke_void_return_wrapper::__call<…>(<lambda> &) __functional_base:348
std::__function::__alloc_func::operator()() functional:1546
std::__function::__func::operator()() functional:1720
std::__function::__value_func::operator()() const functional:1873
std::function::operator()() const functional:2548
juce::AudioProcessorValueTreeState::Parameter::valueChanged(float) juce_AudioProcessorValueTreeState.cpp:75
juce::AudioParameterFloat::setValue(float) juce_AudioParameterFloat.cpp:87
juce::AudioProcessorParameter::setValueNotifyingHost(float) juce_AudioProcessor.cpp:1508
juce::setValueAndNotifyIfChanged(juce::AudioProcessorParameter &, float) juce_VST3_Wrapper.cpp:633
juce::JuceVST3EditController::Param::setNormalized(double) juce_VST3_Wrapper.cpp:759
Steinberg::Vst::EditController::setParamNormalized(unsigned int, double) vsteditcontroller.cpp:178
juce::JuceVST3EditController::paramChanged(int, unsigned int, double) juce_VST3_Wrapper.cpp:1195
juce::JuceVST3EditController::audioProcessorParameterChanged(juce::AudioProcessor *, int, float) juce_VST3_Wrapper.cpp:1217
juce::AudioProcessorParameter::sendValueChangedMessageToListeners(float) juce_AudioProcessor.cpp:1584
juce::AudioProcessorParameter::setValueNotifyingHost(float) juce_AudioProcessor.cpp:1509
DelayEngine::parameterChanged(const juce::String &, float) DelayEngine.h:263
<lambda>::operator()(juce::AudioProcessorValueTreeState::Listener &) const juce_AudioProcessorValueTreeState.cpp:165
juce::ListenerList::call<…>(<lambda> &&) juce_ListenerList.h:124
juce::AudioProcessorValueTreeState::ParameterAdapter::LockedListeners::call<…>(<lambda> &&) juce_AudioProcessorValueTreeState.cpp:195
juce::AudioProcessorValueTreeState::ParameterAdapter::parameterValueChanged(int, float) juce_AudioProcessorValueTreeState.cpp:165
<lambda>::operator()() const juce_AudioProcessorValueTreeState.cpp:93
decltype(std::__1::forward<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(fp)()) std::__1::__invoke<juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&>(juce::AudioProcessorValueTreeState::ParameterAdapter::ParameterAdapter(juce::RangedAudioParameter&)::'lambda'()&) type_traits:3545
std::__invoke_void_return_wrapper::__call<…>(<lambda> &) __functional_base:348
std::__function::__alloc_func::operator()() functional:1546
std::__function::__func::operator()() functional:1720
std::__function::__value_func::operator()() const functional:1873
std::function::operator()() const functional:2548
juce::AudioProcessorValueTreeState::Parameter::valueChanged(float) juce_AudioProcessorValueTreeState.cpp:75
juce::AudioParameterFloat::setValue(float) juce_AudioParameterFloat.cpp:87
juce::AudioProcessorParameter::setValueNotifyingHost(float) juce_AudioProcessor.cpp:1508
TalAudioProcessor::loadPresetXml(juce::XmlElement *) PluginProcessor.cpp:341
TalAudioProcessor::loadPreset(juce::File) PluginProcessor.cpp:409
TalAudioProcessor::loadPreset(juce::String) PluginProcessor.cpp:459
PresetComponent::nextPreset(int) TalPresetComponent.h:366
PresetComponent::buttonClicked(juce::Button *) TalPresetComponent.h:320
$_33::operator()(juce::Button::Listener &) const juce_Button.cpp:419
juce::ListenerList::callChecked<…>(const juce::Component::BailOutChecker &, $_33 &&) juce_ListenerList.h:153
juce::Button::sendClickMessage(const juce::ModifierKeys &) juce_Button.cpp:419
juce::Button::setToggleState(bool, juce::NotificationType, juce::NotificationType) juce_Button.cpp:195
juce::Button::setToggleState(bool, juce::NotificationType) juce_Button.cpp:160
juce::Button::internalClickCallback(const juce::ModifierKeys &) juce_Button.cpp:363
juce::Button::mouseUp(const juce::MouseEvent &) juce_Button.cpp:490
I wonder why the host also sends parameter change messages to the listeners. It looks like it does it only if I’m changing presets fast.
But I wasn’t able to find anything that triggers this.
Edit: My guess is that this only can happen when we have bidirectionally linked parameters (meta parameters). What is a safe way to implement them?
I have a LINK parameter in my case and delayL and delayR parameters and Knobs. LINK does link them together and i want that always both change in that case. I don’t want to solve this in my UI. This should work also with the host generic UI.