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] p4For C++03 we have this:
§6.7 [stmt.dcl] p4The 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:
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
Classin the assignment. The solution to that are inplace factories: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_ptrcombined with placement new and a static aligned buffer: