- Known issues with singletons
- Reference counting to the rescue!
The singleton pattern, it seems to me, is becoming the new
goto of Software Engineering.
What I mean by that is that I often read and hear people judging code quality based on the use of
singletons or not, usually tagging it as bad code for using the pattern.
I am a supporter of the singleton pattern and I think it can solve some problems very elegantly and in the most succinct manner.
Many times, also, you really don’t have much of a choice about using singletons or not. A very common situation where you have
to use a singleton is when interfacing with Operating System services. For example: memory allocation. Every OS has a
A global function that accesses global data (in the
malloc() example, the program heap) is a singleton in its simplest form.
When writing game software, if you decide to work with OpenGL, then your renderer will be a singleton. The OpenGL API is built entirely around global state (the (in)famous state machine of OpenGL), that is accessed by procedure calls. Trying to build an object oriented framework around OpenGL is a daunting task. I have attempted to do so in several occasions and ended up realizing that the amount of code need to make it work properly is inviable for most games. The best way to work with GL, I have found, is to accept its structured architecture and write your renderer that way, as a singleton.
Known issues with singletons
The most valid arguments against singletons are probably about concurrent access issues (i.e. not being thread-safe) followed by initialization and termination order problems.
Indeed issues with concurrent access are hard to solve. The basic solution is to add a mutex around every method of the singleton, which will kill parallelism. So my approach in the BigGun Engine is the flowing:
If a code path will run in parallel?
- Can I do it without a singleton? If yes, then no singleton.
- OK, need to access that global state, no escape, make the singleton thread-safe with a mutex/semaphore/etc.
Singleton startup and shutdown used to be a problem until I realized there was a simple solution right under my nose from the beginning. I have used reference counting in the BigGun Engine in several places, but it never really occurred to me until very late that I could use it for the singleton classes as well.
In the early stages of development, when the Engine was just an agglomerate of several other test projects, I had all singletons implemented in one of two ways: Monostate singletons and “classic” GoF singletons.
The monostate singleton is a class with all of its methods and data declared as
The class cannot be instantiated at all and its methods must be called via the
:: scope resolution operator.
This is the simplest way you can write a singleton. One of its downsides is that initialization and shutdown
are explicit, so you have to call
Logger::Shutdown() manually at some point.
This will quickly lead to initialization/termination order problems. For example, lets say we have another
Logger needs the
FileSystem to open its log file, however the
also wishes to log every file that is opened/closed. Which one do we initialize first?
To fix the initialization problem, one might resort to the “classic” singleton that employs lazy-initialization.
GetInstance() method is the only static method in the logger now. It can be called without a logger instance,
because it is static. When
GetInstance() is first called, it will initialize the logger global. This fixes the
initialization order problem of the
Logger example, but the termination order is now unknown.
C++ static and global variable destruction order is arbitrary, so if your singletons require a particular shutdown
order you are still going to have a bad time.
Reference counting to the rescue!
After many attempts to bring order to the initialization/termination chaos with flags and checks everywhere, I finally realized my approach was fundamentally wrong. The main problem was that interdependence between singletons was implicit, while it should be explicit. What I needed was to make every singleton explicitly maintain a reference to the other singletons it relied on to function properly. By making these instances reference-counted the termination order is always correct. With the use of smart pointers, it is also automatic.
An example from the Engine:
In the BigGun Engine, I have three helper singletons that interface with the underlaying Operating System:
Each is a reference counted type, with a
Create() static method. Initialization is explicit,
Create() pointers to the singletons a particular type depends on, if any.
So for example when the
PlatformManager needs to log a message/error, it doesn’t call a global method on
but instead uses its local pointer to the logger singleton. Thanks to the reference counting, the termination order
works flawlessly, with the
Logger being the last singleton to shutdown in the entire Engine.
Initialization of these three singleton happens with:
NOTE 1: What I call the “BigGun Engine” is a series of learning projects and demos I have collected over the years.
It will keep evolving into a full scale Game Engine eventually. Once I feel it is “old” enough, I will probably make it
available to the world as open source
NOTE 2: I would like to recommend a look at this site. It has some very nice elaborations on the singleton pattern and some other very neat and ingenious solutions. Definitely a must read.