Confusion about deadlock in AudioProcessorParameter class after update to JUCE 6

I’m updating an older plugin project from JUCE 5 to JUCE 6. After the update, the plugin now runs into a deadlock on the message thread here

Going up the call stack quite a bit (45 sub-function calls), I’m reaching this member function call, called on the same AudioProcessorParameter instance

Which shows me that the lock is already held on the message thread. According to the CriticalSection documentation which says

If the lock is already held by the caller thread, the method returns immediately

the thread should not wait here. Still it does. A simple test application could not reproduce this issue.

Note that I do see the possible danger here in deleting a listener while iterating over the listeners, this will be definetively addressed. But for now, I’d really like to understand why this deadlock happens here. Anything stupid that I might overlook?

Are you absolutely sure that the same lock is being locked in both functions here? If you have multiple parameters, it’s possible you might be calling ‘removeListener’ on a separate parameter to the one which called sendValueChangedMessageToListeners.

Yeah, turns out that it was a different lock in the end and a parameter change coming in from a different thread held it – so a classic deadlock in the end but a really really hidden one – took me hours of going through legacy code mostly not written by me :grimacing:

Tbh it would have really surprised me if there had been a real issue with the CriticalSection here that wouldn’t have been discovered before…

But as a question arising from that, is there any clever way of debugging which thread holds the lock for the underlying pthread_mutex in such scenarios that does not involve the manual search I had to perform here?

Not particularly clever, but you could adjust the implementation of CriticalSection::enter to store the result of std::this_thread::get_id() into a data member after successfully gaining the lock. Then, when the deadlock ocurrs, you can inspect this data member to find which thread locked the mutex.