I am aware that this question has been asked and answered in different forms over the years, but I find the previous Q&As about it too tied to specific examples and not providing me with a full understanding of why, and in what scenarios, dlls can produce multiple instances of the same singleton. I am struggling to come up with a minimal reproducible example myself.
Given a singleton in the rough form:
class Singleton
{
private:
static Singleton* singleton;
public:
Singelton(); //
~Singleton(); // Not shown for brevity.
static Singelton* getSingleton();
};
Singleton* Singleton::getSingleton()
{
if (!singleton)
{
singleton= new Singleton;
}
return singleton;
}
This is compiled in to a dll (I'll call is SingletonAndOtherStuff.dll) which is intended for loading via LoadLibrary by other apps.
This next bit is where I start to get confused.
Within a separate primary app (I'll call it app.exe), SingletonAndOtherStuff.dll is loaded via LoadLibrary in two places. What situations could cause multiple copies of the static variable m_instance to appear?
- In the case of diamond pattern, would this lead to two of the singleton?:
app.exe
/ \
static/shared_lib static/shared_lib
\ /
SingletonAndOtherStuff.dll
- If I had multiple copies of the exact same dll on disk, would that lead to multiple copies of the singleton?
app.exe
/ \
/ \
static/shared_lib static/shared_lib
| |
SingletonAndOtherStuff.dll SingletonAndOtherStuff.dll
- If there were other combinations or longer chains of
STATICandSHAREDlibraries betweenapp.exeandSingletonAndOtherStuff.dll, could that cause multiple instances of the singleton?
I am just looking for an explanation for what would cause this pattern to fall over in the situation where the dll is loaded twice.
Okay, let's break this down piece by piece. This dilemma you're facing is quite common among developers when dealing with shared libraries, and the presence of singletons just adds another layer of complexity. Thanks for giving a detailed context; I'll tackle each segment.
First off, our singleton pointer, being static, has its place reserved in memory as soon as our DLL is loaded. This space remains unique, ensuring our singleton stays singular if the DLL is only loaded once.
Here's the thing: even if you repeatedly call LoadLibrary for the same DLL within the same process, the OS doesn't load it afresh each time. It's smart about it and just ups its internal reference count. This ensures our singleton isn't cloned.
The pattern you've mapped out:
Even if both those static/shared libraries beckon the
SingletonAndOtherStuff.dll, it remains one and the same in memory. So, our buddysingletonstays one of a kind.Now, consider this setup:
If you've got two identical DLLs, but they're sitting at different addresses on your disk and both get loaded, the OS sees them as distinct entities. This means they each get their own spot in memory, causing our
singletonto be replicated. Yes, you'd end up with two singletons.It doesn't matter how convoluted the library chain gets. The key thing? How many times, and from which spots on your disk, the
SingletonAndOtherStuff.dllgets loaded. Different locations mean different singleton instances.To wrap up: the singleton behaves as expected when our DLL is invited once from a single spot within one process. But if you're pulling in the same DLL from multiple addresses, even if they're carbon copies, you'll get multiple singletons. It's a quirky aspect of shared libraries, and it's essential to have a grasp on these dynamics when diving into such waters.