Here is a piece of code that is DCL (double-checked locking) implemented by ‘acquire-release’ semantics in C++. The code is as follows:
std :: atomic <Singleton *> Singleton :: m_instance;
std :: mutex Singleton :: m_mutex;
Singleton * Singleton :: getInstance () {
Singleton * tmp = m_instance.load (std :: memory_order_acquire); // 3
if (tmp == nullptr) {
std :: lock_guard <std :: mutex> lock (m_mutex);
tmp = m_instance.load (std :: memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton; // 1
m_instance.store (tmp, std :: memory_order_release); // 2
}
}
return tmp;
}
On https://en.cppreference.com/w/cpp/atomic/memory_order, the interpretation of memory_order_release is: A store operation with this memory order performs the release operation: no reads or writes in the current thread can be reordered after this store. All writes in the current thread are visible in other threads that acquire the same atomic variable.
My understanding is: load-store, store-store can not be reordered, but did not say that other-store can not be reordered.
So I think: '1' includes not only read and write instructions, but also call instruction, then the call instruction may be reordered behind '2'; then '3' may get an unsafe 'tmp' pointer.
Let me describe the above paragraph again:
Disassemble ‘1’ into the following two possible pseudo-instructions:
tmp = allocate ();
call Singleton constructor (tmp); // 4
I think ‘4’ may be reordered after ‘2’. After one thread executes ‘2’, then another thread completes ‘3’ and obtains the tmp pointer. At this time, the tmp pointer is an unsafe Singleton pointer.
So I have this question: Is the above code thread-safe?