Background: One of the problems with using a local static variable in a function as an implementation of the singleton pattern is that if more than one thread calls the function for the first time at the same time, the initialisation of the static variable could be done twice.
My question is, if you wrap the initialisations of the static variables in a critical section, will that prevent the double initialisation from happening? Example:
CRITICAL_SECTION cs;
Class get_class_instance() {
EnterCriticalSection(&cs);
// is the initialisation of c done inside the critical section?
static Class c = Class(data);
LeaveCriticalSection(&cs);
return c;
}
Or is the initialisation done magically (not at the point of the declaration/initialisation), like the initialisation of variables member before the beginning of a constructor?
My question is specifically about pre-C++11 since, as per Xeo's answer, C++11 takes care of this by itself.
C++11 removes the need for locking. Concurrent execution shall wait if a static local variable is already being initialized.
§6.7 [stmt.dcl] p4
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
For C++03 we have this:
§6.7 [stmt.dcl] p4
The zero-initialization (8.5) of all local objects with static storage duration (3.7.1) is performed before any other initialization takes place. A local object of POD type (3.9) with static storage duration initialized with constant-expressions is initialized before its block is first entered. An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope (3.6.2). Otherwise such an object is initialized the first time control passes through its declaration;
The last part is important, since it applies to your code. When control first enters get_class_instance()
, it first passes through the initialization of the critical section, then through the declaration of the singleton (as such will initialize it inside the critical section), and then will pass through the deinitialization of the critical section.
So from a theoretical point of view, your code should be safe.
Now, this can be improved though, as to not enter the critical section on every function call. The basic idea of @Chethan is sound, so we'll base it on that. However, we're going to avoid the dynamic allocation too. For that, however, we're relying on Boost.Optional:
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = Class(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
Boost.Optional avoids the default initialization, and the double check avoids entering the critical section on every function call. This version however introduces a call to the copy constructor of Class
in the assignment. The solution to that are inplace factories:
#include <boost/utility/in_place_factory.hpp>
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = boost::in_place(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
My thanks go to @R. Martinho Fernandes and @Ben Voigt who collaborated to this final solution. If you're interested in the process, feel free to take a look at the transcript.
Now, if your compiler supports some of the C++11 features already, but not the static initialization stuff, you can also use std::unique_ptr
combined with placement new and a static aligned buffer:
#include <memory> // std::unique_ptr
#include <type_traits> // alignment stuff
template<class T>
struct destructor{
void operator(T* p) const{
if(p) // don't destruct a null pointer
p->~T();
}
};
Class& get_class_instance() {
typedef std::aligned_storage<sizeof(Class),
std::alignment_of<Class>::value>::type storage_type;
static storage_type buf;
static std::unique_ptr<Class, destructor> p;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!p)
p.reset(new (&buf[0]) Class(data));
LeaveCriticalSection(&cs);
inited = true;
}
return *p;
}