13

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?

gexicide
  • 38,535
  • 21
  • 92
  • 152
  • 6
    If you can use C++11 just ignore the whole double checked locking business and use either static local variables or `std::call_once`. – R. Martinho Fernandes Sep 06 '12 at 14:12
  • Are static locals initialized lazily? And about `call_once`: How does this ensure that call once will not write the not fully created reference to the variable? – gexicide Sep 06 '12 at 14:14
  • 4
    yes, static locals are initialized lazily in a thread-safe manner. And `call_once` ensures the subject is only ever called once; and that no other call to `call_once` returns before the one that actually executes the function returns (you can read more here http://en.cppreference.com/w/cpp/thread/call_once). How it does that is up to the implementation. These two things basically exist so you don't want to bother with writing more bugs double-checked locking implementations. – R. Martinho Fernandes Sep 06 '12 at 14:16
  • Static local variables must surely be one of the most elegant solutions. – Kerrek SB Sep 06 '12 at 14:19
  • still, call_once might not yield the performance benefits of the double-checked-locking pattern. It only ensures one time execution. However, the static local var thingy could be the solution. – gexicide Sep 06 '12 at 14:19
  • Note that, as Yogi Berra said: *In theory, theory and practice are the same. In practice they aren't*. There are some issues implementing the C++11 memory model in practice in some platforms. – David Rodríguez - dribeas Sep 06 '12 at 14:21
  • Is the thread-safe semantics of static local variables a C++11 only feature? Because I found a blog post that argues that it is indeed NOT thread safe and is even required to be that way by the specification: http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx – gexicide Sep 06 '12 at 14:21
  • @gexicide Yes, it's only in C++11. – R. Martinho Fernandes Sep 06 '12 at 14:23
  • great, thanks! Seems that C++ is going into the right direction with the new standard. Very elegant solution! – gexicide Sep 06 '12 at 14:25

2 Answers2

18

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.

Grizzly
  • 19,595
  • 4
  • 60
  • 78
  • 11
    This is insane. Why, oh why, would you ever return a pointer if it can **never** be `null` ? – Matthieu M. Sep 06 '12 at 15:21
  • 1
    @MatthieuM.: Mainly because it makes people less inclined to copy the underlying object. Of course a well designed singleton **shouldn't** have a copy constructor, but still. I really don't see how return by reference vs. return by value matters in that case. Therefore I would hardly call that insane. – Grizzly Sep 06 '12 at 23:14
  • 2
    @Grizzly: If an object should not be copyable, it is up to the object to enforce that. If an object should have an instance that is globally accessible, there should be a function to handle that (like yours). These two things are separate, and there's no reason to combine them. This is why the Singleton pattern is stupid. – GManNickG Sep 07 '12 at 03:00
  • 1
    Insane because some pople might delete MySingleton::GetInstance, or might if ( MySingleton::GetInstance() ) {}. It makes more sense to return a reference and set copy ctor/assignment to deleted. Or if it has to be a pointer make the dtor private. – paulm Apr 25 '14 at 22:59
  • Doesn't answer question (although it certainly provides a good answer to a different question: "How to efficiently initialize a global singleton in a thread-safe manner"). – BeeOnRope Aug 19 '17 at 22:03
3

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;
}
LWimsey
  • 6,189
  • 2
  • 25
  • 53
  • In practice, you'd want to declare your `mtx` and `instance_p` variables as globals outside of the function, rather than as statics within the function, since otherwise you are paying the price for the compiler's internal checks about the initialization of `mtx` and `instance_p` on _every_ call, defeating the point of double-checked locking (since performance-wise you might as well just declare the singleton as a static then). – BeeOnRope Aug 19 '17 at 22:08
  • You might want to add that you basically never want to write this code though. Grizzly's answer is more concise plus the compiler might insert more magic in the future to make it faster than this code. – gexicide Aug 24 '17 at 16:01
  • @gexicide Of course this is low level stuff that is normally reserved for libraries,etc.. but you literally asked for an implementation.. "_Is there a way to write a fully thread-safe double checked locking pattern in C++? If so, how does it look like_" – LWimsey Aug 24 '17 at 21:59