5

I have known that mutex can also bring the effect as memory barrier from here: Can mutex replace memory barriers, but I always see there is an memory barrier using in c++ singleton example as below, is the memory barrier unnecessary?

Singleton* Singleton::getInstance() {
     Singleton* tmp = m_instance.load(std::memory_order_relaxed);
     std::atomic_thread_fence(std::memory_order_acquire);        
     if (tmp == nullptr) {
         std::lock_guard<std::mutex> lock(m_mutex);               // using mutex here
         tmp = m_instance.load(std::memory_order_relaxed);
         if (tmp == nullptr) {
             tmp = new Singleton;
             assert(tmp != nullptr);    
             std::atomic_thread_fence(std::memory_order_release); // using memory barrier here
             m_instance.store(tmp, std::memory_order_relaxed);
         }
     }
     return tmp;
 }
woder
  • 647
  • 5
  • 12
  • 1
    You don't need all that stuff at all, just do as described in Scott Meyer's _C++ Singleton Pattern_ (the dupe). It's lazy evaluated, and already comes with thread safety guaranteed.. – πάντα ῥεῖ Oct 28 '20 at 15:11
  • thanks, but I also want to figure out whether the memory barrier is necessary or not @πάνταῥεῖ – woder Oct 28 '20 at 15:33
  • Pthread_mutexes, which are often used as the underpinning for C++ locks, define lock and unlock operations as address space wide barriers; so in that instance, you would not require the fences. Does your implementation of std::lock_guard document that guarantee? The standard (sic) seems uncharacteristically mute on the subject. – mevets Oct 28 '20 at 15:50
  • You will benefit more from reading on how memory models work than asking about particular examples. That said, it usually isn't suggested to touch any atomics other than sequential consistent. – Passer By Oct 28 '20 at 16:17
  • _@woder_ _"but I also want to figure out whether the memory barrier is necessary or not"_ As mentioned, it's not needed. Scott Meyer's Singleton Pattern doesn't need any locks or barriers. But well, @Nathan decided to reopen and unduplicate your question, despiete all the answers you wanted/needed are already there IMHO. – πάντα ῥεῖ Oct 28 '20 at 16:22
  • @πάνταῥεῖ `Scott Meyer's Singleton Pattern` uses static to initialize object is good and simple, but problems come when there are a specify initialize order request among different single instances, and there are another solution using using pthread_once to create single instance , whatever, I am not looking for how to create a single instance but just wondering the mutex and memory barrier – woder Oct 29 '20 at 01:29
  • @mevets thanks for your comment, it is on the point – woder Oct 29 '20 at 01:35
  • Is there a reason to use two operations (`std::atomic_thread_fence(std::memory_order_release); m_instance.store(tmp, std::memory_order_relaxed); `) instead of a release store? – curiousguy Nov 12 '20 at 19:04

1 Answers1

1

If you can use C++11, you do not need to program your own protection.

As also referenced here, all the needed stuff is already part of C++11. Copied from there:

For the singleton pattern, double-checked locking is not needed:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. — § 6.7 [stmt.dcl] p4

Singleton& GetInstance() {
  static Singleton s;
  return s;
}

The implementation will provide a memory barrier or whatever to protect your concurrent access. So keep it simple as given in the example!

Klaus
  • 24,205
  • 7
  • 58
  • 113
  • `Scott Meyer's Singleton Pattern` uses static to initialize object is good and simple, but problems come when there are a specify initialize order request among different single instances, and there are another solution using using pthread_once to create single instance , whatever, I am not looking for how to create a single instance but just wondering the mutex and memory barrier, thanks for your answer – woder Oct 29 '20 at 01:29