How to implement multithread safe singleton in C++11 without using <mutex>

Viewed 93480

Now that C++11 has multithreading I was wondering what is the correct way to implement lazy initialized singleton without using mutexes(for perf reasons). I came up with this, but tbh Im not really good at writing lockfree code, so Im looking for some better solutions.

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//
# include <atomic>
# include <thread>
# include <string>
# include <iostream>
using namespace std;
class Singleton
{

public:
    Singleton()
    {
    }
static  bool isInitialized()
    {
        return (flag==2);
    }
static  bool initizalize(const string& name_)
    {
        if (flag==2)
            return false;// already initialized
        if (flag==1)
            return false;//somebody else is initializing
        if (flag==0)
        {
            int exp=0;
            int desr=1;
            //bool atomic_compare_exchange_strong(std::atomic<T>* obj, T* exp, T desr)
            bool willInitialize=std::atomic_compare_exchange_strong(&flag, &exp, desr);
            if (! willInitialize)
            {
                //some other thread CASed before us
                std::cout<<"somebody else CASed at aprox same time"<< endl;
                return false;
            }
            else 
            {
                initialize_impl(name_);
                assert(flag==1);
                flag=2;
                return true;
            }
        }
    }
static void clear()
{
    name.clear();
    flag=0;
}
private:
static  void initialize_impl(const string& name_)
{
        name=name_;
}
static  atomic<int> flag;
static  string name;
};
atomic<int> Singleton::flag=0;
string Singleton::name;
void myThreadFunction()
{
    Singleton s;
    bool initializedByMe =s.initizalize("1701");
    if (initializedByMe)
        s.clear();

}
int main()
{
    while (true)
    {
        std::thread t1(myThreadFunction);
        std::thread t2(myThreadFunction);
        t1.join();
        t2.join();
    }
    return 0;
}

Note that clear() is just for testing, real singleton wouldnt have that function.

6 Answers
template<class T> 
class Resource
{
    Resource<T>(const Resource<T>&) = delete;
    Resource<T>& operator=(const Resource<T>&) = delete;
    static unique_ptr<Resource<T>> m_ins;
    static once_flag m_once;
    Resource<T>() = default;

public : 
    virtual ~Resource<T>() = default;
    static Resource<T>& getInstance() {
        std::call_once(m_once, []() {
            m_ins.reset(new Resource<T>);
        });
        return *m_ins.get();
    }
};
#pragma once

#include <memory>
#include <mutex>

namespace utils
{

template<typename T>
class Singleton
{
private:
    Singleton<T>(const Singleton<T>&) = delete;
    Singleton<T>& operator = (const Singleton<T>&) = delete;

    Singleton<T>() = default;

    static std::unique_ptr<T> m_instance;
    static std::once_flag m_once;

public:
    virtual ~Singleton<T>() = default;

    static T* getInstance()
    {
        std::call_once(m_once, []() {
            m_instance.reset(new T);
        });
        return m_instance.get();
    }

    template<typename... Args>
    static T* getInstance2nd(Args&& ...args)
    {
        std::call_once(m_once, [&]() {
            m_instance.reset(new T(std::forward<Args>(args)...));
        });
        return m_instance.get();
    }
};

template<typename T> std::unique_ptr<T> Singleton<T>::m_instance;
template<typename T> std::once_flag Singleton<T>::m_once;

}

This version complies to be concurrent free where c++11 standard is not guaranteed to be 100% supported. It offers also a flexible way to instantiate the "owned" instance. Even if the magic static word is enough in c++11 and greater the developer may have the necessity to get much more control over the instance creation.

Related