Search code examples
c++multithreadingstatic-initialization

VC++6 thread safe static initialization


I'll start off by saying that I'm already aware that in C++11 standard, static local initialization is now threadsafe. However, I am still required to maintain compatibility with Microsoft Visual C++ 6, so C++11 behavior isn't applicable.

I have a static library which uses a handful of static variables. I ran into issues with static variables getting used before they were initialized (single threaded):

class A
{
private:
    static A Instance;
public:
    static A& GetInstance() { return Instance; }
};

// And then from a different file:

A.GetInstance();

A.GetInstance() would return an uninitialized instance. So I followed this advice http://www.cs.technion.ac.il/users/yechiel/c++-faq/static-init-order-on-first-use-members.html and moved all my static variables into local methods.

class A
{
public:
    static A& GetInstance()
    {
        static A Instance;
        return Instance;
    }
};

I thought that fixed the problem, but now I'm finding out that stuff isn't always getting initialized properly because I create other threads during startup.

Raymond Chen described the problem in 2004: https://blogs.msdn.microsoft.com/oldnewthing/20040308-00/?p=40363 , but nobody seemed to have any solutions. The only solutions anyone mentions are to use mutexes to prevent initialization from multiple threads. But this seems like a chicken and egg problem. Every type of mutex that I'm aware of requires some kind of initialization before it can be used. How can I make sure it gets initialized before I use it for the first time. I guess I'd have to make it static local. But how do I ensure it get initialized from one thread?

If I could ensure that I have one memory location initialized to a known value before anything else is initialized, I could use interlocked operations on it to spin wait to bootstrap my whole initialization. Is there any way to ensure one memory location is in a known state from multiple threads before any other initialization occurs? Or any kind of synchronization that can be done without the chicken and egg problem?


Solution

  • The usual solution to this problem is to use a static object that can be zero-initialized or constant-initialized combined with atomic operations to "bootstrap" yourself into a position where you can call more complex initialization safely.

    Zero and constant initialization is guaranteed to happen before non-constant initialization and since it effectively happens simultaneously it doesn't rely on order of initialization.

    Use a Lazy-Initialized Object

    A very simple example would use a zero-initialized pointer to the global static instance, that indicates if the static has been initialized, like so:

    class A
    {
    private:
        volatile static A* Instance;  // zero-initialized to NULL
    public:
        static A& GetInstance() {
            A* inst = Instance;
            if (!inst) {
                A* inst = new Instance(...);
                A* cur = InterlockedCompareExchange(&Instance, newInst, 0);
                if (cur) {
                  delete inst;
                  return *cur;
                }
            }
            return *inst;
        }
    };
    

    The downside of the above approach is that two (or more) one A object might be created, if two (or more) threads both initially see A::Instance as null. The code correctly selects only one A object to be the true static global returned to all callers and the others are simply silently deleted, but this may be a problem when it isn't even possible to create more than one Instance object in a process (e.g., because it backed by some fundamentally singleton resource, perhaps a handle to some hardware resource). There is also be some wasted work if more than one Instance is created, which might matter if the creation process is expensive.

    This pattern is sometimes called racy single-check.

    Use a Lazy-Initialized Mutex

    A better solution that avoids the above pitfalls would be to use a mutex to guard the creation of the singleton. Of course, now mutex initialization has the same ordering problem, but we can use the above trick to fix that (and we know it's OK to create more than one mutex object).

    class MutexHolder
    {
    private:
        volatile static CRITICAL_SECTION* cs;  // zero-initialized to NULL
    public:
        static CRITICAL_SECTION* get() {
            A* inst = cs;
            if (!inst) {
                CRITICAL_SECTION* inst = new CRITICAL_SECTION();
                InitializeCriticalSection(inst);
                CRITICAL_SECTION* cur = InterlockedCompareExchange(&cs, newInst, 0);
                if (cur) {
                  DeleteCriticalSection(inst);
                  delete inst;
                  return *cur;
                }
            }
            return *inst;
        }
    };
    
    class A
    {
    private:
        static MutexHolder mutex;
        static A* Instance;  // zero-initialized to NULL
    public:
        static A& GetInstance() {
            A* inst;
            CRITICAL_SECTION *cs = mutex.get();
            EnterCriticalSection(cs);
            if (!(inst = Instance)) {
                inst = Instance = new A(...);
            }
            EnterCriticalSection(cs);
            return inst;
        }
    };
    

    Here the MutexHolder is a re-usable wrapper around the Windows CRITICAL_SECTION object which performs lazy and thread-safe initialization inside the get() method, and can be zero-initialized. This MutexHolder is then used as a classic mutex to protect the creation of the static A object inside A::GetInstance.

    You can make GetInstance faster at the cost of some complexity with the use of double-checked locking: rather than getting the CRITICAL_SECTION unconditionally, first check if Instance is set (like the first example) and then return it directly if it is.

    InitOnceExecuteOnce

    Finally, if you are targeting Windows Vista or later, Microsoft has added a ready-made tool which handles this directly: InitOnceExecuteOnce. You can find an worked example here. This is approximately analogous POSIX's to pthead_once and works because initialization is performed using the constant INIT_ONCE_STATIC_INIT.

    In your case it would look something like:

    INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT;
    A* g_AInstance = 0;  
    
    BOOL CALLBACK MakeA(
        PINIT_ONCE InitOnce,       
        PVOID Parameter,           
        PVOID *lpContext)
    {
        g_AInstance = new A(...);
        return TRUE;
    }
    
    class A
    {
    private:
    
    public:
        static A& GetInstance() {
            // Execute the initialization callback function 
            bStatus = InitOnceExecuteOnce(&g_InitOnce,          
                                MakeA,   
                                NULL,                 
                                NULL);          
            assert(bStatus);
            return *g_AInstance;
        }
    };        
    

    Raymond Chen wrote a blog entry about this function which also makes for good reading.