Leak detector fires before object is deleted


#1

I currently get a false positive leak detection from the leak detector. I try to break down the overall structure as easy as possible, but it is a bit complicated:

  • Class A inherits from ReferenceCountedObject and has a member of the type DynamicLibrary. A declares the usual typedef juce::ReferenceCountedObjectPtr<A> Ptr; to make it possible to simply write A::Ptr
  • Class B inherits from BBase and has a A::Ptr member and gets an A::Ptr as constructor argument which it assigns to the member A:Ptr
  • Class C has a private static OwnedArray<BBase> member

Now the following situation can occur in a simple command line application:

  • A temporary instance of A is created to check if the dynamic library is available at runtime
  • In case of success, the A::Ptr from above is passed to a B constructor which is invoked through a call to new B
  • This instance of B is then added to the OwnedArray in C through a static member function of C
  • When the end of the program is reached, the leak detector fires complaining about a leaked instance of DynamicLibrary. A breakpoint placed in the destructor of A which owns the DynamicLibrary instance is not hit before the leak detector fires.
  • When commenting out the leak detection for the DynamicLibrary the destructor breakpoint is hit, A and with it the DynamicLibrary is destructed as it should. So the leak detector fires before the destruction of A through the static OwnedArray.

Now what is the best way to work around this error? I don’t want to modify the JUCE codebase under any circumstances as my module should be used by others. I also don’t really want to change a lot in my own quite complex codebase that works just fine and error free. Is there any option to opt-out the leak detection for a certain instance or something like that?


#2

Simply do not declare the class in question with JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR ?


#3

Well, it is a JUCE class (DynamicLibrary) that is declared with JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR and as I said, I don’t want to modify the JUCE codebase at all cost


#4

You can continue the code on a jassertfalse, so you will know, if the destructor would have been hit afterwards. But I doubt that.

That sound fishy to me, I would have a look into that one. A static member function cannot have access to a member variable, so maybe this OwnedArray is in fact a static variable?


#5

Oops my bad :slight_smile:


#6

The owned array is a static member variable, as said above:

And as I said, the leak detectors jassert is hit before the destructor is called. If I continue after the jassert, the destructor will be called.


#7

That is a difficult problem. The leak detector is a static object, and if your object is a static one as well, then it is hard to predict the order of destruction, e.g. if they were defined in different translation units.
Maybe there is a way to enforce the destruction of the class C, but I don’t know, how a clean version would look like.
IMHO static objects are best to be avoided, there are better solutions nowadays.


#8

Thank you Daniel, I suspected the answer would be like that :confused:

Of course I could create a static cleanup method to be manually called at the end of the program, however this would be quite unintuitive and would not feel quite “modern C++ like”.

In general I’m open to re-think my design, but I have no idea what are those better solutions that exist nowadays?

My use-case basically is interaction with hardware, I’m building a quite complex hardware abstraction layer for some high data-rate RF interfaces and check for the availability of the hardware drivers at runtime (–> therefore the dynamic library) and then store all driver instances that could be found on the specific system in that Owned Array. It would be bad if two instances of those hardware drivers could be created at the same time, as there is always only one physical hardware setup connected to the system, so a single static instance seemed like the best solution possible to me.


#9

Yes, sorry about my strong opinion against static variables. There might be a way to make it work properly, but I personally wouldn’t touch global objects with a 10 foot pole.

If you can guarantee, that it is always one process, that kind of solution will be fine. Since there should be a central point, having it owned there (worst case in main()) and handing over a reference is probably the safest approach.
Another solution could be a SharedResourcePointer. You can hold one instance at the very beginning, that will keep the object alive, and you can pull a SharedResourcePointer at any time.

But if there is the chance, that different processes try to talk to the hardware, you need some kind of InterprocessLock. The PhysicalTopologySource in the juce_blocks_basics module uses this approach. Granted, it’s not the most beautiful piece of code to look at.

If you want different processes to be able to share the hardware, I don’t see another way than creating a little server, that connects to the hardware and offers an InterProcessConnection interface.

But I don’t have the ultimate wisdom, maybe there are other solutions as well…
Good luck