It happens when the first singleton is instantiated. It’s the singleton’s lock that causes the crash. Larger context is during dynamic initialization of global variables very early in the startup. The singleton class inherits from DeletedAtShutdown.
What’s happening here? I assume many others have successfully used VS 2022 already, so something weird must begoing on here. What can I look for?
This sounds a little bit similar to the issue described in the linked thread, although that seemed to affect std::mutex rather than the Win32 mutex:
Regardless, if you have any library dependencies, it might be worth checking that you’re statically linking the most recent C++ runtime, or have the most recent dynamic runtime installed and available.
There’s some useful information about toolset compatibility on this page.
Thanks. The proposed workaround and solution didn’t help though.
The issue is probably that CriticalSection is not yet ready for use when C++ is initializing global application variables like, for example of this kind:
Too bad. This wasn’t an issue with VS 2019 as far as I remember. I’m using lots of these variables e.g. for icons and other resources, in order to make sure misspellings are caught at compile time. If you look them up in a hash map by name you never learn about typos.
Not sure how to work around this. Maybe singletons could operate lock-free during initialization of global variables?
Using JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL looks like a workaround. But not for fonts. The Juce TypefaceCache is a regular singleton that crashes during startup.
Mutexes are not necessary while an app is initializing variables on a single thread. Still, if classes are involved that happen to use one that should not crash.
namedGlobal is initialisated at static initialisation time, as is the singletonHolder. If these statics are in different translation units, then their initialisation order is unspecified, so namedGlobal may be initialised before the singleton holder for MySingleton, leading to a crash.
To mitigate, you could:
init namedGlobal lazily:
const auto& getNamedGlobal() {
static const auto result = MySingleton::getInstance()->makeString();
return result;
}
or, ensure the correct initialisation order by declaring dependent statics in the same translation unit:
// .h a
extern const String namedGlobal;
// .h b
class MySingleton { /* etc */ };
// .cpp
JUCE_IMPLEMENT_SINGLETON (MySingleton)
// only access MySingleton after its holder has been initialised
const String namedGlobal = MySingleton::getInstance()->makeString();
Your proposed solution works. Unfortunately, maintaining a long list with dozens of variables across two files (header and cpp) is something to avoid. I’d rather have the entire list in the header only.
Not sure how to accomplish that yet. Might have to run my own header-only singleton wrapper. At least the cause is clear now.
Here’s a header-only simple singleton solution that works so far. The boilerplate can be wrapped in a macro. It has the advantage of not being dependent on initialization order, i.e. genuine “lazy” evaluation.
Static variables with local scope are extremely handy as an alternative to global variables. They spare you the separate “implementation” part.
The current JUCE implementation with the SingletonHolder as a global variable separate from the header causes some stress here. For example, it’s impossible to store an object in a header-defined global variable that makes use of a Font, directly or indirectly. TypefaceCache is just not ready at the time and crashes.
If SingletonHolder was a static inline variable in the header, it would be initialised early and reliably before any user code gets involved. As said, getIInstance() should be callable at any time.
I assume it’s for historical reasons when static inline wasn’t available pre C++17, correct? Any reason for not moving it to the header now that C++17 is minium requirement? It shouldn’t affect any code out there and make things much more robust during startup.
That is true and actually one of the most painful things when dealing with static in JUCE (any struct that might contain, say, juce::String in it, must be contained in a JUCE singleton instead of a regular singleton.
To my knowledge, if you’re creating a static function that fetches a singleton it would mean the singleton is not shared with other translation units and gets duplicated for each TU.
Not if you use a static inlinevariable in class scope (as of C++17). These get linked as a single instance across all compilation units. I’m using that all the time.
However - you would probably get the exact same effect - just with the extra safety of lazy creation and if you replace your static members with members that use SharedResourcePointer<T>
You can also have one class that holds the state without the singleton pattern. Passing a reference to other classes.
Most use singletons because they can read/change the state from everywhere. This has several drawbacks as you already noticed when reporting this bug. It’s also hard to write tests for classes that are using singletons.
But this is just my opinion. In plugins, we shouldn’t use them anyway.
Singletons are extremely valuable for plugins for anything that has to be shared across instances.
Examples are all the graphic assets (Images, Fonts, etc), most of which JUCE is already providing singletons for (like ImageCache).
There are more extreme scenarios. For example we make sample-based plugins where the samples have to be held be a Singleton to not eat up the user’s memory.
That’s also true for performance related objects like thread pools.
Yes, this is true. Sharing resources inside a plug-in makes sense, but I don’t want to share these resources between instances. I would prefer a clean state for every new plugin instance and keep things simple.
We now have some popular hosts that start the plugin in dedicated processes. In this case, you may experience different behavior in different DAWs.
For me, sharing things between instances adds complexity I don’t want.
Edit: I have tons of issues because of the static default look and feel class.