realloc() as part of a buffer reassignment for an
AudioProcessor, and I get a crash (sometimes in a totally different area, like
realloc() downstream of
repaint()) from the reallocation code.
I put the reallocation in a message thread using
AsyncUpdater, but this did not solve my issue. Is there a way to check for a lock before running reallocation code? I can’t see what else the issue could be.
Tips for best practice are appreciated!!
Edit: crash details
This is the stack trace I’ve got on the crash:
juce::HeapBlock<std::complex<float>, false>::realloc<int> juce_HeapBlock.h:293
and this is the
realloc() line that is crashing (not the actual
/** Re-allocates a specified amount of memory.
The semantics of this method are the same as malloc() and calloc(), but it
uses realloc() to keep as much of the existing data as possible.
template <typename SizeType>
void realloc (SizeType newNumElements, size_t elementSize = sizeof (ElementType))
data = static_cast<ElementType*> (data == nullptr ? std::malloc (static_cast<size_t> (newNumElements) * elementSize)
: std::realloc (data, static_cast<size_t> (newNumElements) * elementSize));
^^^ - crashes here
Sweet, I got it.
CriticalSection lock; // .h
const ScopedLock sl (lock); // .cpp - at start of reallocation method
seems to solve the issue
So you are reallocating asynchronous on the message thread and accessing the
HeapBlock on another thread (the audio thread?) simultaniously? If it’s like that than this sounds like a bad design. However, a good solution to this depends on the context.
A simple one is to create a
CriticalSection object that has to be locked by a
ScopedLock for every kind of access to the
HeapBlock. For many use-cases this is a good idea, but in case you are working on the audio thread, this could lead to the audio thread being blocked my the message thread that holds the lock but has a lower priority. This would be called a priority inversion and can lead to audio dropouts. In this case, I’d advise you to have two heap blocks, one that is accessed from the audio thread and one that is not. You can then safely resize the inactive block on the message thread or any other and copy over the content from the active one. When all is done, you can simply swap them, which is a short operation
Yes, thank you!
Previously I had missed that I was using this reallocation directly on the audio thread, and yes, it caused issues. I had moved all the reallocation to the message thread, but still had the crash I originally posted.
ScopedLock solved the issue, thanks for pointing it out! Although I originally got the result straight from the open-sourced code I’m using
Thanks for pointing out the swapping operation as well. It may come in useful, although, in my case right now - when
ScopedLock is used, does this prevent the AudioThread from trying to access the heaps being modified? (to avoid mid-resize accesses)
It prevents it in a nasty way: it will let the processBlock wait until you are finished doing your allocation!
That is exactly what PluginPenguin described as priority inversion.
An allocation can take an infinite amount of time, because the memory management needs to lock as well, so you have no idea for whom you are actually waiting.
Alright, that makes sense. Thanks for pointing it out!