0

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?

BobJao
  • 79
  • 1
  • 6
  • FWIW, you get all of this built in with `Singleton& Singleton :: getInstance () { static Singleton instance; return instance; }` – NathanOliver May 08 '20 at 12:59
  • @NathanOliver That's right, c ++ 11 guarantees the thread safety of static local variables. But I want to know if this code is thread safe? – BobJao May 08 '20 at 13:05

1 Answers1

2

Yes, it is safe!

If the acquire-load returns null (i.e., the singleton is not yet initialized) you acquire the mutex. Inside the mutex the reload can be relaxed since the modifications of m_instance is protected by the mutex anyway, i.e., if some other thread has already initialized the singleton, then the mutex-release of that thread must have happend before our mutex-acquire operation, so it is guaranteed that we see the updated m_instance.

If the acquire-load (1) "sees" the value written by the release-store (2), the two operations synchronize-with each other thereby establishing a happens-before relation, so you can safely access the object tmp points to.

Update
The release-store is also protected by the mutex, and it is not possible that a part of the initialization of tmp is reordered with the store. In general one should avoid to argue about possible reorderings. The standard says nothing about if/how operations can be reordered. Instead, it defines the (inter-thread)-happens-before relation. Any reorderings that a compiler may perform are merely a result of applying the rules of the happens-before relations.

If the acquire-load (1) loads the value written by the release-store (2), the two operations synchronize-with each other thereby establishing a happens-before relation, i.e., (2) happens-before (3). But since (1) is sequenced-before (2) and the happens-before relation is transitive, it has to be guaranteed that (1) happens-before (3). Thus it is not possible to reorder (1) (or parts of it) with (2).

mpoeter
  • 2,574
  • 1
  • 5
  • 12
  • “the mutex-release of that thread must have happend before our mutex-acquire operation”,it`s right.but the call instruction may be reordered behind '2',so,we get an unsafe 'tmp' pointer from '3'. – BobJao May 08 '20 at 13:46
  • Sorry, I don't really understand what you mean? The release-store (2) is also protected by the mutex. – mpoeter May 08 '20 at 13:48
  • My expression is not very good, I re-edited the question, you could look at it again. – BobJao May 08 '20 at 15:18
  • I have updated my answer with more details - I how it is clear now why your code is safe. – mpoeter May 08 '20 at 17:21
  • Thank you very much, we should use Happens-before rule programming. – BobJao May 09 '20 at 03:54
  • What about fields of `Singleton` class ? will they be visible correctly after getting value in `tmp` ? Is that because of `memory_order_acquire` at 3 ? – Ashish Negi Aug 31 '22 at 21:56