Błażej Michalik
Błażej Michalik

Reputation: 5085

Differences between static declarations in singletons

(Code taken from book: http://gameprogrammingpatterns.com/ by Robert Nystrom)

In the book above the author comes with two different ways of making a singleton class:

First one:

class FileSystem
{
public:
  static FileSystem& instance()
  {
    // Lazy initialize.
    if (instance_ == NULL) instance_ = new FileSystem();
    return *instance_;
  }

private:
  FileSystem() {}

  static FileSystem* instance_;
};

And second one:

class FileSystem
{
public:
  static FileSystem& instance()
  {
    static FileSystem *instance = new FileSystem();
    return *instance;
  }

private:
  FileSystem() {}
};

Later he states that the second one is more proper way to do this, since it is thread safe, while the first one is not.
What makes the second one thread safe?
What is the difference in static declarations between these two?

Upvotes: 2

Views: 85

Answers (3)

Mohit Jain
Mohit Jain

Reputation: 30489

In former case, if two threads try to create the instance at the same time, 2 (or more) copies of singleton objects might be created. (if **instance_** is observed NULL by both and both create a new instance). (Even worse, thread creating first instance may get a different value of instance in subsequent calls)

While second one uses static initialization and object is constructed when the function is invoked for the first time. So it is guaranteed by the compiler that static FileSystem *instance = new FileSystem(); would be executed at most once during the lifetime of program single and thus atmist single copy of object would exist at any time.

This makes the later design thread safe for C++11 and onwards complian C++ compilers. Though the design may not be safe for in C++03 and C++98 implementation.


Another downside of former design is, the object can not be destructed while in later design it can be destructed by changing the typeof instance_ to static FileSystem. i.e.

static FileSystem& instance()
{
  static FileSystem instance;
  return instance;
}

Related: Is Meyers implementation of Singleton pattern thread safe?

Upvotes: 1

Lingxi
Lingxi

Reputation: 14987

The first version is not thread-safe for multiple threads may try to read and modify instance_ concurrently without any synchronization, which leads to a race condition.

The second version is thread-safe since C++11. Quoted from cppreference (the Static local variables section):

If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once)

With this guarantee, modification to instance happens only once, and there is no problem with concurrent reads.

Still, the second version is not thread-safe until C++11.

Upvotes: 3

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726929

In the first code snippet setting the instance_ pointer to a singleton is an assignment. It does not get any special treatment from the compiler. In particular, it could be done multiple times if instance() is invoked from concurrent threads.

This creates a problem when two concurrent threads try evaluating instance_ == NULL, and get a true. At this point both threads create a new instance, and assign it to the instance_ variable. The first pointer assigned to instance_ is leaked, because the second thread immediately overrides it, rendering the object inaccessible.

In the second code snippet setting the instance pointer is initialization. The compiler guarantees that initialization of a static variable will be done at most once, regardless of the number of threads that invoke instance() concurrently. Essentially, the guarantee that there is at most one singleton in the system is provided by the compiler, without any explicit code for handling concurrency.

Upvotes: 1

Related Questions