AsyncUpdater::triggerAsyncUpdate() locks when the message posts to the message queue:
bool MessageManager::postMessageToSystemQueue (MessageBase* message)
{
jassert (appDelegate != nil);
appDelegate->messageQueue.post (message);
return true;
}
//from juce_osx_MessageQueue.h
void post (MessageManager::MessageBase* const message)
{
messages.add (message);
wakeUp();
}
private:
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> messages;
ReferenceCountedArray::add() locks here and uses the CriticalSection above:
ObjectClass* add (ObjectClass* newObject)
{
const ScopedLockType lock (getLock());
values.add (newObject);
if (newObject != nullptr)
newObject->incReferenceCount();
return newObject;
}
ParameterAttachment::parameterValueChanged() calls triggerAsyncUpdate() (potentially on the audio thread) when the parameter has changed:
void ParameterAttachment::parameterValueChanged (int, float newValue)
{
lastValue = newValue;
if (MessageManager::getInstance()->isThisTheMessageThread())
{
cancelPendingUpdate();
handleAsyncUpdate();
}
else
{
triggerAsyncUpdate();
}
}
A possible solution is to use an atomic and a timerCallback():
class ParameterAttachment : private AudioProcessorParameter::Listener,
private Timer
{
public:
ParameterAttachment(...)
{
//same as existing ctor implementation
startTimerHz(60);
}
private:
Atomic<bool> parameterChanged { false };
void parameterValueChanged(int, float newValue) override
{
if( newValue != lastValue )
{
lastValue = newValue;
parameterChanged.set(true);
}
}
void timerCallback() override
{
if( parameterChanged.compareAndSetBool(false, true) )
if( setValue ) //the std::function<void (float)> parameterChangedCallback
setValue( lastValue );
}
}
This will solve the issue of ParameterAttachment locking the audio thread whenever a parameter change happens and the parameterValueChanged callback is called from the audio thread.
