Search code examples
c++synchronizationstdatomic

C++ Guarantee that no nullptr pointer is retrieved using atomic operations


It is the following piece of code

#include <memory>
#include <iostream>
#include <atomic>
class A
{
    public:
        A * get()
        {
            if(nullptr == ptr.load(std::memory_order_acquire))
            {
                ptr.store(new A(),std::memory_order_release);
            }    
            return ptr.load(std::memory_order_acquire);
        }
        inline static  std::atomic<A*> ptr = nullptr; 
};

int main()
{
    return 0;
}

Reading some documents the code above does not guaranteed that the ptr is not null when get is called . Actually the problem is when ThreadA check that ptr is null enter in CS and before call store ThreadB , enters in CS , allocation memory and returns. When threadA stores the values overwrite the previous one value. When get return the ptr is null. My question is why thread A does not see the updated value ?


Solution

  • To make the posted code thread safe, you have to use a mutual exclusion mechanism for the allocation in addition to a std::atomic for the pointer. The most common choice would be locking a std::mutex. Here is what that code might look like,

    class AMutex
    {
    public:
        AMutex * get()
        {
          if (auto p = ptr.load(); p)
              return p;
    
            std::lock_guard lock(mtx);
            if (not ptr.load())
                ptr.store(new AMutex());
    
            return ptr.load();
        }
    
        inline static std::atomic<AMutex*> ptr = nullptr;
        static std::mutex mtx;
    };
    

    As an aside, the code you posted looks very close to a Singleton except that the get function is not static. If a Singleton is the goal, here is a simple, thread-safe, standards-compliant option,

    class A2 {
    public:
        static A2 *get() {
            static A2 instance;
            return &instance;
        };
    };