Double lock check in C ++ 11?

Here is an example Java example from http://www.ibm.com/developerworks/java/library/j-dcl/index.html

public static Singleton getInstance() { if (instance == null) //#4 { synchronized(Singleton.class) { //#1 if (instance == null) //#2 instance = new Singleton(); //#3 } } return instance; } 

This seems to be unsafe because # 3 can set the instance to non-null before the constructor is executed, while the other thread will check the instance at # 4, it will not be empty and will return an instance that was not properly constructed.

Apparently, using a function variable will not help, because it can be optimized or just done in a way that also sets the value to the instance when we don't want it.

I thought the easiest way is to have the new Singleton(); function new Singleton(); so that it is completed before assigning it to an instance. Now the problem is, how can I say that a C ++ function should NOT be inline? I think Singleton* make_singleton() volatile should do this, but I'm sure I'm wrong.

+7
source share
2 answers

I will ignore the singleton bits for a while and assume that you need this for lazy initialization, and not for stupid things like single player games.

I suggest forgetting the double lock check. C ++ provides a very useful tool for this kind of situation in the form of std::call_once , so use this.

 template <typename T> struct lazy { public: // needs constraining to prevent from doing copies // see: http://flamingdangerzone.com/cxx11/2012/06/05/is_related.html template <typename Fun> explicit lazy(Fun&& fun) : fun(std::forward<Fun>(fun)) {} T& get() const { std::call_once(flag, [this] { ptr.reset(fun()); }); return *ptr; } // more stuff like op* and op->, implemented in terms of get() private: std::once_flag flag; std::unique_ptr<T> ptr; std::function<T*()> fun; }; // --- usage --- lazy<foo> x([] { return new foo; }); 
+25
source

This is exactly the type of situation for which atoms are intended. By storing the result in atomic, you know that the compiler cannot sort any critical stores or operations after the atom is installed. Atomics both for emitting primitives of processor instructions to ensure the necessary consistent consistency (for example, for cache coherency across all cores), and for the compiler, which should preserve the semantics (and, therefore, limit the types of reordering that it can perform). If you use atomic here, it doesn't matter if the function is enabled, because any compiler insert will have to preserve the semantics of the atom itself.

You may also be interested in searching in std::call_once , which is also designed for this situation, or rather, for a situation where several threads can do something, but just one of them should do it.

+2
source

All Articles