Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: Safe double checked locking for lazy initialization. Possible?

I have read many questions considering thread-safe double checked locking (for singletons or lazy init). In some threads, the answer is that the pattern is entirely broken, others suggest a solution.

So my question is: Is there a way to write a fully thread-safe double checked locking pattern in C++? If so, how does it look like.

We can assume C++11, if that makes things easier. As far as I know, C++11 improved the memory model which could yield the needed improvements.

I do know that it is possible in Java by making the double-check guarded variable volatile. Since C++11 borrowed large parts of the memory model from the one of Java, so I think it could be possible, but how?

like image 489
gexicide Avatar asked Sep 06 '12 14:09

gexicide


People also ask

Can double-checked locking fail?

Double-Checked Locking is widely cited and used as an efficient method for implementing lazy initialization in a multithreaded environment. Unfortunately, it will not work reliably in a platform independent way when implemented in Java, without additional synchronization.

Is double-checked locking thread safe?

Double-checked locking is a common pattern for lazy initialization of a field accessed by multiple threads.

Can the double-checked locking fail on single processor system?

Ans. There is no mapping of single ton with number of processor of the system. So double check locking will not fail depending on number of processor.

Why double-checked locking in singleton?

This double check lock is only necessary if you are worried about many threads calling the singleton simultaneously, or the cost of obtaining a lock in general. Its purpose is to prevent unnecessary synchronization, thereby keeping your code fast in a multi-threaded environment.


2 Answers

Simply use a static local variable for lazily initialized Singletons, like so:

MySingleton* GetInstance() {
  static MySingleton instance;
  return &instance; 
}

The (C++11) standard already guarantees that static variables are initialized in a threadsafe manner and it seems likely that the implementation of this at least as robust and performant as anything you'd write yourself.

The threadsafety of the initialization can be found in §6.7.4 of the (C++11) standard:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

like image 120
Grizzly Avatar answered Oct 19 '22 14:10

Grizzly


Since you wanted to see a valid DCLP C++11 implementation, here is one.

The behavior is fully thread-safe and identical to GetInstance() in Grizzly's answer.

std::mutex mtx;
std::atomic<MySingleton *> instance_p{nullptr};

MySingleton* GetInstance()
{
    auto *p = instance_p.load(std::memory_order_acquire);

    if (!p)
    {
        std::lock_guard<std::mutex> lck{mtx};

        p = instance_p.load(std::memory_order_relaxed);
        if (!p)
        {
            p = new MySingleton;
            instance_p.store(p, std::memory_order_release);
        }
    }

    return p;
}
like image 23
LWimsey Avatar answered Oct 19 '22 14:10

LWimsey