JUCE_AUTORELEASEPOOL BAD_ACCESS in VST destructor


#1

OS X 10.11, Reaper64

In my host vst plugin I have an AudioProcessorGraph that can load other vst’s. Say I load a child vst into the graph, open the child’s ui, and then remove the host vst. This causes a BAD_ACCESS in the destructor of the ScopedAutoReleasePool that gets destroyed at the end of ~JuceVSTWrapper():

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff86e8416f objc_release + 31
1 com.apple.CoreFoundation 0x00007fff8f7cbc12 _CFAutoreleasePoolPop + 50
2 com.apple.Foundation 0x00007fff8e94a9f5 -[NSAutoreleasePool release] + 146
3 com.myCompany.hostPlugin 0x0000000112139860 juce::ScopedAutoReleasePool::~ScopedAutoReleasePool() + 20
4 com.myCompany.hostPlugin 0x000000011221df8e JuceVSTWrapper::~JuceVSTWrapper() + 234
5 com.myCompany.hostPlugin 0x000000011221c8fe JuceVSTWrapper::~JuceVSTWrapper() + 14
6 com.myCompany.hostPlugin 0x000000011221b940 AudioEffect::dispatchEffectClass(AEffect, int, int, long long, void, float) + 48**
7 com.cockos.reaper 0x00000001003219a4 VST_HostedPlugin::~VST_HostedPlugin() + 308
8 com.cockos.reaper 0x000000010034b2b0 FxDsp::~FxDsp() + 544
9 com.cockos.reaper 0x000000010036c876 FxChain::dialogProc(HWND__*, unsigned int, unsigned long, long) + 8918
10 ??? 0x00010036a53e0000 0 + 281709677248512

I’ve narrowed it down to the graph’s destructor where it calls clearRenderingSequence() & deletes all the graph ops. If I prevent the ProcessBufferOp that’s associated with the child vst from being deleted here the crash does not occur (but assume would then leak).

I would guess that there’s some sort of double ownership going on here and something is getting deleted twice…

The really odd part is that the crash does not occur if the child vst’s ui is not instantiated.

Does anybody have tips on how to debug with an NSAutoReleasePool? What exactly is the purpose of having such a pool in ~JuceVSTWrapper()?


#2

Looks like it might be somehow trying to release a dead object… That’s not supposed to be possible in obj-C, but I’ve seen similar things when you try to delete plugins without giving them enough time to shut down before their DLL is unloaded.

Maybe try modifying your code so that when deleting a plugin, you first remove its window, stop it playing, then wait a second before deleting the plugin itself. (You need to keep the event thread running while you wait, i.e. use a Timer or something asynchronous, to give it chance to clean up its UI detritis)


#3

Hmm, how exactly do I pause the event thread to let it run other messages when all of this deletion process is occurring on the event thread itself? Is there some way to “freeze” the current message for a period of time?

Thanks for the help!


#4

It looks like this issue is very similar to all of these:






I’ve tried injecting MessageManager::getInstance()->runDispatchLoopUntil (1000); in many different places but I still get the crash.


#5

If it’s your host then you don’t need to use hacks like runDispatchLoopUntil. Like I said above, you can do it cleanly, and just use a Timer or something to delete the plugin later.


#6

My host is a plugin itself, so I’m competing with all the shutdownJuce_GUI() & activePlugins arrays in the plugin wrapper code files. It seems that any timers running go dead when the gui is shutdown.

Why wouldn’t runDispatchLoopUntil() work in this context?


#7

You should never run a modal loop in a plugin. (Or an app, except in really unusual circumstances)


#8

It seems that there’s not really an elegant way to solve this issue for plugins that are hosting other plugins.

The main issue is that the host sort of expects all of the plugin’s allocations to be freed once the callback that deleted the plugin finishes.

Once deleted by the host, there’s no guarantee that a plugin’s instruction code will still be around in memory, is there? I’ve tried creating an “async deleter” object on the heap that uses the Timer class to wait a millisecond and then deletes the plugin. It seems to work but I feel like it’s just a matter of timing and in many use cases would probably crash (eg when say the host quickly deletes one instance and creates another via switching projects)

Does anybody else have any ideas for such an issue?


#9

Running into the same issue here - did you ever find a better solution or did you stick with the async deleter?

cheers!


#10

Hmm, I switched gigs shortly after so I don’t have access to that particular code anymore. I don’t think we ever came up with a more elegant solution, though.