EDIT: I think I found the cause of the problem, skip to the next reply.
By attaching the debugger like you suggested I was able to get a bit more insight into what happens.
I placed breakpoints inside:
MessageManager::MessageManager()
void TimerThread::handleAsyncUpdate()
void TimerThread::run()
TimerThread::TimerThread()
TimerThread::~TimerThread()
After Pro Tools starts scanning plugins the following sequence is called:
MessageManager::MessageManager()
TimerThread::TimerThread()
Then Pro Tools offers the dashboard window and I’m able to open an empty session. After that I instantiate our plugin after which the following calls are made:
MessageManager::MessageManager()
After that, when I close Pro Tools I get several leaked object asserts:
- *** Leaked objects detected: 2 instance(s) of class SharedResourcePointer
- *** Leaked objects detected: 1 instance(s) of class TimerThread
- *** Leaked objects detected: 1 instance(s) of class Thread
- *** Leaked objects detected: 3 instance(s) of class WaitableEvent
- *** Leaked objects detected: 1 instance(s) of class AsyncUpdater
The interesting thing here is that I expect more calls to be made when the plugin scanning process happens. I expect something like this:
MessageManager::MessageManager()
TimerThread::TimerThread()
void TimerThread::handleAsyncUpdate()
void TimerThread::run()
But this is not what happens.
Without diving in too deep, what seems to be happening is something like this:
- Plugin scanning starts
- MessageManager gets created
- A Timer gets created (my
MainPluginProcessor
which is a subclass of juce::AudioProcessor
also inherits from Timer which seems to trigger the creation of the TimerThread
)
- A TimerThread gets created which triggers an async update to start the
TimerThread
thread running
- Pro Tools destroys the plugin instance and takes
MessageManager
with it
- For some reason a Timer sticks around keeping the
SharedResourcePointer
with TimerThread
alive. I think this is where the core problem is.
- After loading Pro Tools and instantiating the plugin a
MessageManager
gets created but not a TimerThread
, since that one already exists.
So we seem to basically end up with a case where the TimerThread
is instantiated, but not started because the MessageManager
is destroyed before it gets a chance to callback the async updater.
When I switch to one commit before the problematic commit, TimerThread
gets properly DeletedAtShutdown
and the JUCE leak detector doesn’t report any leaks when shutting down Pro Tools:
juce::Timer::TimerThread::~TimerThread() juce_Timer.cpp:41
juce::Timer::TimerThread::~TimerThread() juce_Timer.cpp:40
juce::Timer::TimerThread::~TimerThread() juce_Timer.cpp:40
juce::DeletedAtShutdown::deleteAll() juce_DeletedAtShutdown.cpp:75
juce::shutdownJuce_GUI() juce_MessageManager.cpp:507
juce::ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() juce_MessageManager.cpp:515
juce::ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() juce_MessageManager.cpp:515
GetEffectDescriptions(AAX_ICollection *) juce_audio_plugin_client_AAX.cpp:2694
-- redacted multiple lines --
main 0x0000000104bd6a58
start 0x00000001801090e0
Back to the problematic commit, I added some extra breakpoints to:
MainPluginProcessor::MainPluginProcessor()
MainPluginProcessor::~MainPluginProcessor()
MessageManager::~MessageManager()
Which results in the following order of execution:
MessageManager::MessageManager
:
juce::MessageManager::MessageManager() juce_MessageManager.cpp:29
juce::MessageManager::MessageManager() juce_MessageManager.cpp:28
juce::MessageManager::getInstance() juce_MessageManager.cpp:51
juce::initialiseJuce_GUI() juce_MessageManager.cpp:499
juce::ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() juce_MessageManager.cpp:514
juce::ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() juce_MessageManager.cpp:514
GetEffectDescriptions(AAX_ICollection *) juce_audio_plugin_client_AAX.cpp:2673
-- redacted multiple lines --
main 0x0000000100f7aa58
start 0x00000001801090e0
TimerThread::TimerThread
:
(as a result of instantiating MainPLuginProcessor)
juce::Timer::TimerThread::TimerThread() juce_Timer.cpp:34
juce::Timer::TimerThread::TimerThread() juce_Timer.cpp:33
std::construct_at[abi:v160006]<…>(juce::Timer::TimerThread *) construct_at.h:38
std::allocator_traits::construct[abi:v160006]<…>(std::allocator<…> &, juce::Timer::TimerThread *) allocator_traits.h:304
std::__shared_ptr_emplace::__shared_ptr_emplace[abi:v160006]<…>(std::allocator<…>) shared_ptr.h:284
std::__shared_ptr_emplace::__shared_ptr_emplace[abi:v160006]<…>(std::allocator<…>) shared_ptr.h:276
std::allocate_shared[abi:v160006]<…>(const std::allocator<…> &) shared_ptr.h:995
std::make_shared[abi:v160006]<…>() shared_ptr.h:1004
juce::SharedResourcePointer::Weak::lockOrCreate() juce_SharedResourcePointer.h:156
juce::SharedResourcePointer::SharedResourcePointer() juce_SharedResourcePointer.h:172
juce::SharedResourcePointer::SharedResourcePointer() juce_SharedResourcePointer.h:90
juce::Timer::Timer() juce_Timer.cpp:293
MainPluginProcessor::MainPluginProcessor() MainPluginProcessor.cpp:41
MainPluginProcessor::MainPluginProcessor() MainPluginProcessor.cpp:46
createPluginFilter() MainPluginProcessor.cpp:552
juce::createPluginFilterOfType(juce::AudioProcessor::WrapperType) juce_CreatePluginFilter.h:35
AAXClasses::getPlugInDescription(AAX_IEffectDescriptor &, const AAX_IFeatureInfo *) juce_audio_plugin_client_AAX.cpp:2589
GetEffectDescriptions(AAX_ICollection *) juce_audio_plugin_client_AAX.cpp:2682
-- redacted multiple lines --
main 0x0000000100f7aa58
start 0x00000001801090e0
MainPluginProcessor::MainPluginProcessor
:
MainPluginProcessor::MainPluginProcessor() MainPluginProcessor.cpp:47
MainPluginProcessor::MainPluginProcessor() MainPluginProcessor.cpp:46
createPluginFilter() MainPluginProcessor.cpp:552
juce::createPluginFilterOfType(juce::AudioProcessor::WrapperType) juce_CreatePluginFilter.h:35
AAXClasses::getPlugInDescription(AAX_IEffectDescriptor &, const AAX_IFeatureInfo *) juce_audio_plugin_client_AAX.cpp:2589
GetEffectDescriptions(AAX_ICollection *) juce_audio_plugin_client_AAX.cpp:2682
-- redacted multiple lines --
main 0x0000000100f7aa58
start 0x00000001801090e0
MainPluginProcessor::~MainPluginProcessor
:
MainPluginProcessor::~MainPluginProcessor() MainPluginProcessor.cpp:120
MainPluginProcessor::~MainPluginProcessor() MainPluginProcessor.cpp:119
MainPluginProcessor::~MainPluginProcessor() MainPluginProcessor.cpp:119
std::default_delete::operator()[abi:v160006](juce::AudioProcessor *) const unique_ptr.h:65
std::unique_ptr::reset[abi:v160006](juce::AudioProcessor *) unique_ptr.h:297
std::unique_ptr::~unique_ptr[abi:v160006]() unique_ptr.h:263
std::unique_ptr::~unique_ptr[abi:v160006]() unique_ptr.h:263
AAXClasses::getPlugInDescription(AAX_IEffectDescriptor &, const AAX_IFeatureInfo *) juce_audio_plugin_client_AAX.cpp:2661
GetEffectDescriptions(AAX_ICollection *) juce_audio_plugin_client_AAX.cpp:2682
-- redacted multiple lines --
main 0x0000000100f7aa58
start 0x00000001801090e0
MessageManager::~MessageManager
:
juce::MessageManager::~MessageManager() juce_MessageManager.cpp:42
juce::MessageManager::~MessageManager() juce_MessageManager.cpp:36
juce::deleteAndZero<…>(juce::MessageManager *&) juce_Memory.h:40
juce::MessageManager::deleteInstance() juce_MessageManager.cpp:65
juce::shutdownJuce_GUI() juce_MessageManager.cpp:508
juce::ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() juce_MessageManager.cpp:515
juce::ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() juce_MessageManager.cpp:515
GetEffectDescriptions(AAX_ICollection *) juce_audio_plugin_client_AAX.cpp:2694
-- redacted multiple lines --
main 0x0000000104f9ea58
start 0x00000001801090e0
btw: I never hit the assert inside Timer::startTimer
:
// If you're calling this before (or after) the MessageManager is
// running, then you're not going to get any timer callbacks!
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS