How to Implement Multithread Safe Singleton in C++11 Without Using <Mutex>

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

C++11 removes the need for manual locking. Concurrent execution shall wait if a static local variable is already being initialized.

§6.7 [stmt.dcl] p4

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

As such, simple have a static function like this:

static Singleton& get() {
static Singleton instance;
return instance;
}

This will work all-right in C++11 (as long as the compiler properly implements that part of the standard, of course).


Of course, the real correct answer is to not use a singleton, period.

Do we need mutex for accessing the data field in singleton object in C++11 - Multithreading?

tl;dr: Yes.

C++ 11 "static threadsafe" prevents a race condition in:

static Singleton *single_manager = new Singleton();

Without that, possible issues are:

  • Singleton() instantiated twice
  • different threads seeing different instances of Singleton
  • Singleton initialization not visible to other threads

However, any access to m_data (i.e. both GetData and SetData) need a mutex when they are called from different threads.


Where to lock - should the singleton have a lock inside, or should the caller lock?

(as per comment)

This question has no simple answer.

I would add a lock to the singleton class and lock in GetData / SetData. Rationale: almost always this is the default use case and removes burden from the caller.

But:

The inner lock is not always sufficient, e.g. if you have:

Singleton * s = Singleton::instance();
int x = s->GetData();
x = x + 1;
s->SetData(x);

the caller needs to use an outer lock around the Get/Modify/Set.

The argument against an inner lock is as follows:

  • the inner lock is not sufficient in the general case, i.e.
    there are cases where callers need their own lock
  • when the caller has to lock anyway, the inner lock becomes a performance issue that can be significant.

Conclusion: If you can avoid the get-modify-set scenario for the data at hand, it seems prudent to add an inner lock (e.g. the API could include an IncrementData) method. Clearly document the thread safety of the interface (e.g. "this class is not thread safe and requires external locking" or "individual function calls are threadsafe")


Notes:

And yes: as you already thought: nothing of this is related to the class being a singleton.

If the data is really just an int, an "atomic integer" (e.g. from boost:.atomic) can be used instead. the get/set/modify problem remains, though.

Singleton with multithreads

In C++11, the following is guaranteed to perform thread-safe initialisation:

static Singleton* getSingletonInstance()
{
static Singleton instance;
return &instance;
}

In C++03, a common approach was to use double-checked locking; checking a flag (or the pointer itself) to see if the object might be uninitialised, and only locking the mutex if it might be. This requires some kind of non-standard way of atomically reading the pointer (or an associated boolean flag); many implementations incorrectly use a plain pointer or bool, with no guarantee that changes on one processor are visible on others. The code might look something like this, although I've almost certainly got something wrong:

static Singleton* getSingletonInstance()
{
if (!atomic_read(singletonInstance)) {
mutex_lock lock(mutex);
if (!atomic_read(singletonInstance)) {
atomic_write(singletonInstance, new Singleton);
}
}
return singletonInstance;
}

This is quite tricky to get right, so I suggest that you don't bother. In C++11, you could use standard atomic and mutex types, if for some reason you want to keep the dynamic allocation of you example.

Note that I'm only talking about synchronised initialisation, not synchronised access to the object (which your version provides by locking the mutex in the accessor, and releasing it later via a separate function). If you need the lock to safely access the object itself, then you obviously can't avoid locking on every access.

Thread-safe singleton in C++11

Static variables are NOT allocated on the stack. In the first variant you have a static pointer (a global variable) initialized with a memory obtained from the heap, while in the second one you have whole static object.

Both versions use internal compiler guards (namely __cxa_guard_acquire() and __cxa_guard_release(), which are functionally equivalent to mutex::lock() and mutex::unlock()) to ensure serialized access to a special variable that tells whether or not your global instances are already initialized or not.

Your code:

Foo& getInst()
{
static Foo inst(...);
return inst;
}

will actually look like that after the compilation:

Foo& getInst()
{
static Foo inst; // uninitialized - zero
static guard instGuard; // zero

if (is_initialized(instGuard) == false)
{
__cxa_guard_acquire(instGuard);
// do the initialization here - calls Foo constructor
set_initialized(instGuard);
__cxa_guard_release(instGuard);
}

return inst;
}

So both of your exampes are thread safe.

Thread Safe Singleton Class - Am I doing this Right?

Yours is not idiomatic singleton, because it is still prone to so-called "static initialization order fiasco".

An idiomatic singleton doesn't have a static class member, instead it's instance function looks like

static MySingleton& instance() {
static MySingleton the_ton;
return the_ton;
}

More on the fiasco: static initialization order fiasco

efficient thread-safe singleton in C++

Your solution is called 'double checked locking' and the way you've written it is not threadsafe.

This Meyers/Alexandrescu paper explains why - but that paper is also widely misunderstood. It started the 'double checked locking is unsafe in C++' meme - but its actual conclusion is that double checked locking in C++ can be implemented safely, it just requires the use of memory barriers in a non-obvious place.

The paper contains pseudocode demonstrating how to use memory barriers to safely implement the DLCP, so it shouldn't be difficult for you to correct your implementation.

Is this Singleton Implementation Thread-Safe?

It is hard to beat a simple

class Singleton { ... };

Singleton* get_instance()
{
static Singleton instance; // guaranteed to be thread safe in C++11
return &instance;
}

Any access to the pointer after it has been returned is still not thread safe, but neither is access in your implementation.

Singleton class and thread safety


  1. C++11 compiler makes this thread safe. Static variables can only be created by one single thread.
  2. Other questions about locks both depend on how threads should work with your methods. If you have multiple threads that can do something with one same piece of memory - use locks.

Simple lock can be used with std::unique_lock and std::mutex:

void setNumber(double number) {
static std::mutex _setNumberLock;
std::unique_lock _lock(&_setNumberLock);

// your code
}

Thread safe singleton implementation in C++

No, this is not thread safe because the static local is not guarded in any way. By default a static local is not thread safe. This means you could run into the following issues

  • Constructor for the singleton runs more than once
  • The assignment to the static is not guaranteed to be atomic hence you could see a partial assignment in multi-threaded scenarios
  • Probably a few more that I'm missing.

Here is a detailed blog entry by Raymond Chen on why C++ statics are not thread safe by default.

  • http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx

C++ thread-safe increment with modulo without mutex using std::atomic

Modulo can be safely used after choosing

std::atomic<size_t> mNextBufferIndex;

Buffer* GetNextBuffer()
{
// How to make this operation atomic?
size_t index = mNextBufferIndex ++;

size_t id = index % mTotalBuffers;

// If size could wrap, then re-write the modulo value.
// oldValue keeps getting re-read.
// modulo occurs when nothing else updates it.
size_t oldValue =mNextBufferIndex;
size_t newValue = oldValue % mTotalBuffers;
while (!m_mNextBufferIndex.compare_exchange_weak( oldValue, newValue, std::memory_order_relaxed ) )
newValue = oldValue % mTotalBuffers;
return mBuffers[id ];
}


Related Topics



Leave a reply



Submit