VFLib OncePerSecond crashes host on plugin unload


#1

Hello,

I'm using VFLib in my plugin. I use vf::ManualCallQueue in my audio process. The implementation of the vf::ManualCallQueue class references a complicated memory management object hierarchy, which involves a vf::TimerSingleton that starts a thread that calls a method of a vf::PagedFreeStore once per second. The vf::TimerSingleton is a subclass of vf::RefCountedSingleton that is a subclass of vf::PerformedAtExit which should destroy the singleton and stop the thread when static objects are destroyed. However, i see the host(s) crash with access violation, when they unload my plugin. When i break the debugger (VS2010) at the exception, the once per second thread is active. There are no symbols, since the plugin DLL has been unloaded. It looks like the thread is calling code that has been unloaded. Setting breakpoints reveals that the performAtExit methods are not called and the reference counted singletons aren't destroyed and the vf::TimerSingleton doesn't stop the thread.

Is it possible for the host to unload the plugin DLL without the appropriate destructors of static objects getting called?

Actually, the behaviour is even more confusing, since this happens only when unloading after i've showed a PopupMenu. Simply showing the popup menu is enough. To my knowledge the juce::PopupMenu and the relevant parts of VFLib have nothing to do with each other. I'm really struggling with understanding what is going on here.

Dec 11 14:47:00 <0c23eb18> class AudioProcessorEditor::AudioProcessorEditor::~AudioProcessorEditor:...destroyed editor.
vf::OncePerSecond::TimerSingleton::notify.
vf::OncePerSecond::TimerSingleton::run.
Dec 11 14:47:01 <0ac24848> class AudioProcessor::AudioProcessor::releaseResources:AudioProcessor::releaseResources
Dec 11 14:47:01 <0ac24848> class AudioProcessor::AudioProcessor::~AudioProcessor:Destroying processor...
The thread 'GuiCallQueue' (0x664) has exited with code 0 (0x0).
Dec 11 14:47:01 <0ac24848> class AudioProcessor::AudioProcessor::~AudioProcessor:...destroyed processor.
The thread 'Worker' (0xaac) has exited with code 0 (0x0).
The thread 'Juce Timer' (0xab8) has exited with code 0 (0x0).
*** Leaked objects detected: 2 instance(s) of class WindowsDirectWriteTypeface
*** Leaked objects detected: 2 instance(s) of class Typeface
First-chance exception at 0x774dfc16 in SONARPDR.exe: 0x0000087A: 0x87a.
vf::OncePerSecond::TimerSingleton::notify.
vf::OncePerSecond::TimerSingleton::run.
'SONARPDR.exe': Unloaded 'C:\Users\developer\VstPlugins\MyPlugin\MyPlugin.dll'
'SONARPDR.exe': Unloaded 'C:\Windows\System32\opengl32.dll'
'SONARPDR.exe': Unloaded 'C:\Windows\System32\ddraw.dll'
'SONARPDR.exe': Unloaded 'C:\Windows\System32\dciman32.dll'
'SONARPDR.exe': Unloaded 'C:\Windows\System32\glu32.dll'
'SONARPDR.exe': Unloaded 'C:\Windows\System32\dbghelp.dll'
First-chance exception at 0x5bc2471c in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bc2471c.
First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.
First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.
First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.
First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.

... 1000 or so more of the above ...

First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.
First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.
First-chance exception at 0x5bf6aeb0 in SONARPDR.exe: 0xC0000005: Access violation reading location 0x5bf6aeb0.
First-chance exception at 0x77d31a9c in SONARPDR.exe: 0xC0000005: Access violation writing location 0x02eb0fe4.
Unhandled exception at 0x77d31a9c in SONARPDR.exe: 0xC0000005: Access violation writing location 0x02eb0fe4.

Edit:

I tested by simply commenting out the code that starts/stops the Once Per Second thread and the plugin unloads without crashing. This is not a solution, but i'm pretty sure now that the OncePerSecond class has not been designed to function in a plugin DLL, but is designed with the assumption that all processing is killed by the OS upon unload or something.

I still have no clue about why the PopupMenu makes this problem arise, but i don't believe it has anything to do with the actual cause of this problem.

I'm working on a solution that controls the thread's execution and lifetime. Right now i'm thinking about keeping track of plugin instances and removing the thread in the AudioProcessor destructor when the last instance is destroyed.

Edit:

I think i'm going to hack a cleanup system executed on DLL_PROCESS_DETACH. It's not going to be pretty, but leaving all shutdown and cleanup to destructors of static objects, that may be called in an arbitrary order, in an environment where the host may load/unload the DLL on a whim, doesn't feel right either.

If you have any ideas about how to tackle this, i would really appreciate all help!