I thought this was going to be a simple bug...
Here's the issue:
- Build the latest development branch (I'm on a mac using xcode, but this is probably a cross-platform issue).
- Run it.
- Throw a Spike Viewer in the signal chain.
- Open the Spike Viewer's tab, make sure it has focus, then press a key on your keyboard.
- You'll hit a breakpoint, and find this waiting for you in the debug console:
*** Dangling pointer deletion! Class: KeyPress
JUCE Assertion failure in juce_LeakedObjectDetector.h:71
Bummer! But Ok, I thought, let's fix it. And that's when my weekend plans went out the window.
Long story short, usually the LeakedObjectDetector class template uses one LeakCounter singleton per class that uses the template. So in this case, there's supposed to be one LeakCounter dedicated to counting constructions and deletions of KeyPress objects. But it turns out that singleton's don't behave as you might expect when combined with a plugin architecture - each plugin (and the main app) has its own compilation unit (translation unit), and, as things stand now, we actually get one LeakCounter singleton per class that uses the LeakedObjectDetector template per translation unit. That's crazy. It makes sense, but ugh, crazy. Let's take a closer look at the craziness, just to be sure.
First things first. The issue goes away if you comment out the entire body of SpikeDisplayCanvas::keyPressed. If you then un-comment only the line
KeyPress c = KeyPress::createFromDescription("c");
... the issue returns. So, simply instantiating then deallocating a KeyPress within this method causes a false dangling pointer alert. That's weird. But check out what happens if I modify getCounter to talk to me a bit (code borrowed and modified from here):
--- a/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h
+++ b/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h
@@ -107,6 +107,13 @@ private:
static LeakCounter& getCounter() noexcept
{
static LeakCounter counter;
+
+ static int LeakCounterCounter;
+
+ if (strcmp (getLeakedObjectClassName(), "KeyPress") == 0) {
+ DBG (String::formatted ("%d: function pointer %p, counter object: %p, value: %d", LeakCounterCounter++, getCounter, &counter, counter.numObjects.get ()));
+ }
+
return counter;
}
};
If I do that, then repeat the steps above, I get:
...(omitting output prior to my keypress)
791: function pointer 0x10019fac0, counter object: 0x100942578, value: 25
0: function pointer 0x107485ca0, counter object: 0x1074910e0, value: 0
*** Dangling pointer deletion! Class: KeyPress
JUCE Assertion failure in juce_LeakedObjectDetector.h:71
Different counter objects, different function pointers... different LeakCounterCounters... ugh. If I put a breakpoint on my DBG line and stop there during the offending call, then take a look at the stack to see which ~LeakedObjectDetector is being called, I get:
BasicSpikeDisplay`juce::LeakedObjectDetector<juce::KeyPress>::~LeakedObjectDetector:
... whereas if I do the same during a call to getCounter from any use of KeyPress within the main app, I get:
open-ephys`juce::LeakedObjectDetector<juce::KeyPress>::~LeakedObjectDetector:
You can get the same thing to happen in any one of the plugins, just by creating a KeyPress object. I haven't looked at other objects that use JUCE_LEAK_DETECTOR yet.
So, what to do? We could just set JUCE_CHECK_MEMORY_LEAKS to false for plugins, but that's unsatisfying in a couple ways: it doesn't address the multiple singletons issue, and kills memory leak checking for objects only ever used in a single plugin. It seems like someone with stronger linker-fu than I might have a helpful suggestion here.
(edit: originally included wrong line from stack trace)
I thought this was going to be a simple bug...
Here's the issue:
Bummer! But Ok, I thought, let's fix it. And that's when my weekend plans went out the window.
Long story short, usually the LeakedObjectDetector class template uses one LeakCounter singleton per class that uses the template. So in this case, there's supposed to be one LeakCounter dedicated to counting constructions and deletions of KeyPress objects. But it turns out that singleton's don't behave as you might expect when combined with a plugin architecture - each plugin (and the main app) has its own compilation unit (translation unit), and, as things stand now, we actually get one LeakCounter singleton per class that uses the LeakedObjectDetector template per translation unit. That's crazy. It makes sense, but ugh, crazy. Let's take a closer look at the craziness, just to be sure.
First things first. The issue goes away if you comment out the entire body of
SpikeDisplayCanvas::keyPressed. If you then un-comment only the line... the issue returns. So, simply instantiating then deallocating a KeyPress within this method causes a false dangling pointer alert. That's weird. But check out what happens if I modify getCounter to talk to me a bit (code borrowed and modified from here):
If I do that, then repeat the steps above, I get:
Different counter objects, different function pointers... different LeakCounterCounters... ugh. If I put a breakpoint on my DBG line and stop there during the offending call, then take a look at the stack to see which ~LeakedObjectDetector is being called, I get:
... whereas if I do the same during a call to getCounter from any use of KeyPress within the main app, I get:
You can get the same thing to happen in any one of the plugins, just by creating a KeyPress object. I haven't looked at other objects that use JUCE_LEAK_DETECTOR yet.
So, what to do? We could just set
JUCE_CHECK_MEMORY_LEAKSto false for plugins, but that's unsatisfying in a couple ways: it doesn't address the multiple singletons issue, and kills memory leak checking for objects only ever used in a single plugin. It seems like someone with stronger linker-fu than I might have a helpful suggestion here.(edit: originally included wrong line from stack trace)