Shared Data Initialization

  • Global variable

    • Accessible to all code in the program
  • Static variable at namespace scope

    • Accessible to all code in the translation unit
  • Class member which is declared static

    • Potentially accessible to code which calls its member functions
    • If public, accessible to all code
  • Local variable which is declared static

    • Accessible to all code which calls that function
  • Global variable, Static variable at namespace scope, Static data member of class:

    • All are initialized when the program starts
    • At that point, only one thread is running
    • There cannot be a data race

Static Local Variable

  • Initialized after the program starts
  • When the declaration is reached
void func() {
  // static local variable
  static std::string str("xyz");
}

  • Two or more threads may call the constructor concurrently
  • Is there a data race?

Static Local Variable Initialization before C++11

  • No language support

    • The behavior was undefined
  • Lock a mutex?

    • Required on every pass through the declaration
    • Very inefficient

Static Local Variable Initialization in C++11

  • The behavior is now well-defined

  • Only one thread can initialize the variable

    • Any other thread that reaches the declaration is blocked
    • Must wait until the first thread has finished initializing the variable
    • The threads are synchronized by the implementation
    • No data race
  • Subsequent modifications

    • The usual rules for shared data
    • There will be a data race, unless we protect against one

Singleton Class

  • Used to implement the Singleon design pattern

  • A singleton class has only a single instance

    • e.g. a logger class that maintains an audit trail
  • Its constructor is private

  • The copy and move operators are deleted

    • The program cannot create more objects
  • A static member function returns the unique instance

    • If the instance does not already exist, it is created and initialized
    • Otherwise, the existing object is returned

Classic Singleton Implementation

class Singleton {
  // Pointer to the unique instance
  static Singleton *single;
public:
  // Static member function which returns the unique instance
  static Singleton* get_singleton() {
    if (single == nullptr)
      single = new Singleton;
    return single;
  }
  
  // Class functionality
}

  • This has a data race, in a multi threaded program, instead of getting a unique object, we have several different objects.

C++11 Singleton Implementation


class Singleton {
  // Class functionality
}

Singleton& get_singleton() {
  // Initialized by the first thread that executes this code
  static Singleton single;
  return single;
}

  • The first thread to reach the definition creates the unique instance
    • Subsequent threads use the object created by the first thread
    • The object remains in existence until the program terminates