Crash (pure virt func call) when adding Plugin in ProTools (MacOS)

Hi!

Our AAX plugin crashes whenever it is added to a track in ProTools under MacOS.

Under Windows it does not crash, but there is a memory leak when closing the application, which may be related.

The stack trace of the crash looks like this:

Thread 0 Crashed:: Main Thread Dispatch queue: com.apple.main-thread
Exception Type:    EXC_CRASH (SIGABRT)
Termination Reason:  Namespace SIGNAL, Code 6, Abort trap: 6
Terminating Process: Pro Tools [55188]

0   libsystem_kernel.dylib      __pthread_kill + 8
1   libsystem_pthread.dylib     pthread_kill + 296
2   libsystem_c.dylib           abort + 124
3   libc++abi.dylib             __abort_message + 132
4   libc++abi.dylib             __cxa_pure_virtual + 24
5   MyProject                   std::__1::__shared_count::__release_shared[abi:ue170006]() + 64 (shared_ptr.h:173)
6   MyProject                   std::__1::__shared_weak_count::__release_shared[abi:ue170006]() + 28 (shared_ptr.h:214)
7   MyProject                   std::__1::shared_ptr<juce::CriticalSection>::~shared_ptr[abi:ue170006]() + 64 (shared_ptr.h:773)
8   MyProject                   std::__1::shared_ptr<juce::CriticalSection>::~shared_ptr[abi:ue170006]() + 28 (shared_ptr.h:771)
9   MyProject                   juce::ListenerList<juce::Thread::Listener, juce::Array<juce::Thread::Listener*, juce::CriticalSection, 0>>::~ListenerList() + 44 (juce_ListenerList.h:79)
10  MyProject                   juce::ListenerList<juce::Thread::Listener, juce::Array<juce::Thread::Listener*, juce::CriticalSection, 0>>::~ListenerList() + 28 (juce_ListenerList.h:79)
11  MyProject                   juce::Thread::~Thread() + 220 (juce_Thread.cpp:58)
12  MyProject                   MyClass::ConnectionThread::~ConnectionThread() + 56 (MyClass.cpp:34)
13  MyProject                   MyClass::ConnectionThread::~ConnectionThread() + 28 (MyClass.cpp:34)
14  MyProject                   MyClass::ConnectionThread::~ConnectionThread() + 28 (MyClass.cpp:34)
15  MyProject                   std::__1::default_delete<MyClass::ConnectionThread>::operator()[abi:ue170006](MyClass::ConnectionThread*) const + 60 (unique_ptr.h:68)
16  MyProject                   std::__1::unique_ptr<MyClass::ConnectionThread, std::__1::default_delete<MyClass::ConnectionThread>>::reset[abi:ue170006](MyClass::ConnectionThread*) + 104 (unique_ptr.h:300)
17  MyProject                   MyClass::~MyClass() + 92 (MyClass.cpp:100)

The class MyClass is essentially a copy of JUCE’s InterprocessConnection class, including its nested ConnectionThread class, which inherits from juce::Thread.

Any ideas what the issue could be?

I’ve tried hardening all the destructors, trying to ensure that MyClass::ConnectionThread is stopped upon destruction, using Notify::no to ensure that no callbacks come after the object is gone, detaching the callbacks before destroying to avoid any late callback accessing the instance being destroyed, etc… but nothing has helped.

The juce::InterprocessConnection destructor (and my own MyClass destructor aswell, since its a copy thereof) has a big comment which I initially thought was pointing exactly at my problem:

InterprocessConnection::~InterprocessConnection()
{
    // You *must* call `disconnect` in the destructor of your derived class to ensure
    // that any pending messages are not delivered. If the messages were delivered after
    // destroying the derived class, we'd end up calling the pure virtual implementations
    // of `messageReceived`, `connectionMade` and `connectionLost` which is definitely
    // not a good idea!

So i added the missing disconnect call to the destructor of MyDerivedClass (which derives from MyClass, duh!)

MyDerivedClass::~MyDerivedClass()
{
    // See comment in base class destructor. 
    disconnect(4000, Notify::no);
}

Notice the use of Notify::no, which by the way should be mentioned in the juce::InterprocessConnection destructor, IMO.

Well, this didn’t help.

That sounds like a job for the AdressSanatizer of Xcode and the JUCE_LEAK_DETECTOR.

if you experience problems on windows as well, I’d say you have some kind of ub and the asserts are gonna hit on MacOs as well.

MyClass already uses JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR, but that has not been hit at all so far.

With the Address Sanitizer I am having a classic “crash no longer reproducible when ASan is enabled” scenario, which indicates that the issue is timing-sensitive. Some kind of race condition when tearing down threads, maybe.

I then tried Thread Sanitizer instead, and that gave me 3 warnings in a completely different module. That may point to memory coruption across modules, or may also be totally unrelated.

Stuck at the moment.