Segfault: static objects calling ImageCache::getFromMemory()


#1

I have several class members I converted to statics, but ran into a problem. I am guessing this is because I’m now using some parts of JUCE prior to everything being initialized correctly. Commenting things out, I narrowed it down to this:

In my .hpp file:

In my .cpp file:

(The pointer used in getFromMemory() is a static unsigned char[] generated from another tool and is defined a few lines before myImage, so I know it is valid.)

The segfault this causes is due to a NULL “type” dereferenced in juce::var::~var(): type->cleanUp(value);

juce::var::~var at JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp:370 juce::PNGImageFormat::decodeImage at JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp:425 juce::ImageFileFormat::loadFrom at JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp:81 juce::ImageFileFormat::loadFrom at JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp:104 juce::ImageCache::getFromMemory at JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp:161 backgroundImage at Resources.cpp:119 __static_initialization_and_destruction_0 (__initialize_p=<optimized out>, __priority=<optimized out>) at DoorComponent.cpp:12

To get around it, I’m now using the empty image constructor for my static image, and in my code I do the following to force loading the images I need after main() has started:

if ( Foo::backgroundImage.isNull() ) { Foo::backgroundImage = ImageCache::getFromMemory( ... ); }

Is there a call I can make to force the internals of JUCE to be initialized? If so, I could make such a call at the top of my .cpp which has a bunch of static objects. For example:

static bool finishInitializing = juce::finishStaticInitializations(); Foo::backgroundImage( ImageCache::getFromMemory( ... ) ); // now works since JUCE is initialized

I searched using keywords such as “static” in the doxygen output, but didn’t see anything relevant.


#2

There is a call, but you shouldn’t use it for this.

Basically: don’t use static objects with non-trivial constructors in C++. It’s always going to end up causing trouble. I’d go further and say don’t use static objects at all if you can avoid it, and this certainly looks like a case where you could easily not use a static.


#3

Use static objects like this, and you’re safe (as a bonus, you save memory):

Image & getMyImage()
{
    static Image x(ImageCache::getFromMemory(...));
    return x;
}

#4

Definitely don’t use static objects, and definitely not the way X-Ryl suggested. It might work in some cases but when you have objects dependent on other objects, the order of destruction is not guaranteed. Instead, use a vf::RefCountedSingleton. It is thread safe and correctly handles order of destruction.


#5

Wrong. Read the C++ standard, 3.6.3.1 [basic.start.term]. “The static objects are destructed in the reverse order of construction”.
Unlike global static instance, here the destruction order is guaranteed against any translation unit.
This will not work ONLY if you take a pointer on the returned reference, pass it around and then use it in the end of your software.
Since the compiler must track lifetime of references, so you are safe using this scheme.

The RefCounting idea has a very big drawback, you are serializing the destruction of objects, so your program is going to take eons to close when the user close it (I’m calling it the MFC syndrom).
Typically, if you have “A depends on B depends on C” and the destructor of your global C is called first, then you stall, wait until the destructor of B is called, then A and that, in turns will destruct B, and then C.

In the case of static singleton, the destruction happens globally at after “main()” is done, and can be optimized by the compiler if there is no dependency (and 99% of the time there is none). For most class, even the OP case, it’s a simple reclaiming of the allocated memory since the destructor has no side effects.
BTW, if you look the very common Busybox source code, you’ll notice that they never call free for “lifetime-allocated” buffer, nor do they call malloc. They save space in the binary, in execution time (the kernel is going to reclaim the memory better than you’ll ever do), they use static initialization because it’s faster to only mmap a single page once for all the static objects in your code at the loading stage.


#6

Wrong. Read the C++ standard, 3.6.3.1 [basic.start.term]. “The static objects are destructed in the reverse order of construction”.

I’m late to the party here, but for completeness, the above only applies to individual source files (more specifically, what the standard calls ‘translation units’). Across source files, the initialisation order is undefined:
https://isocpp.org/wiki/faq/ctors#static-init-order