Tuesday, September 9, 2008

Multiple Singleton Instances

After some time, you finally find the problem you've been hunting down - your Singleton's constructor actually is called more than once! This is, in essence a Multiton.

By now, you know what the problem is, but I'll list the possible causes I know about here (assuming a GOF compliant Singleton pattern:

1. Race condition in first call to instance()
This is a constant worry in multithreaded applications. The situation is that two threads call instance() at the same time, in both cases the static/class variable show the instance to be null, so both threads create the instance and assign it, the first of which is a leaked object in a non-GC language.

a. Create your instance in your main thread, before other threads are created. In some cases, this is not possible.
b. Synchronize within the instance() method. Many a developer has turned their nose at this one, because instance() should be as fast as possible. If so, use Double-Checked Locking, which avoids the synchronization as much as possible. A word of caution to Java programmers, there is considerable debate as to whether DCL is reliable in Java.

2. Singleton incorporated into shared library and executable
This is probably the trickiest problems to understand at first. If your singleton is linked into multiple executable modules (shared libraries/dlls and main executable) the static instance reference will be defined multiple times. Shared libraries share code, not data.

This really applies beyond singletons - it effects all static members and global data you use. These global members are "global" to the module.

The fix for this problem is simple: Create a shared library for the singleton itself, so that _instance is defined only one time.

3.C++ Global memory initialization.
As we all become painfully aware, in C++ there are no guarantees of the initialization order of globals defined in separate units. In other words, global initialization had better be as trivial as possible.

If a global object has a non-trivial constructor which invokes instance() on a singleton, or if a global pointer is initialized with an instance() call to a singleton, it opens the program up to the Multiton problem.

The way it could happen is like this:
Global variables being initialized...
Global variable constructor calls instance()
Singleton instance created
Singleton's _instance is initialized (back to NULL)
main() executes
Code calls instance()
New Singleton instance is created

(Big thanks to On Freund for suggesting #3)


Take control of your initialization order by changing, where necessary global objects to global pointers, initialized to NULL, and initialize them in main()

No comments: