Race conditions in static local variables

c++, initialization, multithreading, race-condition, singleton

Solution

This section is outdated. The C++03 standard had no mention of threads, so when C++ implementations added them, they did whatever they wanted to with regards to the thread-safety of language constructs. A common choice was to not ensure thread-safe initialisation of static local variables.

In C++11, local static variables are guaranteed to be initialised exactly once, the first time that the program's control flow passes through their declaration, even if this happens concurrently on multiple threads `6.7/4`:

An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

Even so, this only ensures that initialisation is safe. If you plan to use the returned `FileSystem` from multiple threads simultaneously, `FileSystem` must itself provide threadsafe operations.

Problem

I am currently reading Effective C++. There is a section about using static local variables and it says that if multiple threads access a static variable, there may be a race condition during initialization of that variable. At least that is my interpretation. Is this true? In C# for example, initialization of a class static variable will never have a race condition. For example, can this code have a race condition during the static variable initialization? ``` FileSystem& tfs() { static FileSystem fs; return fs; } ``` Below is the except from the book. Here's the technique applied to both tfs and tempDir: ``` class FileSystem { ... }; // as before FileSystem& tfs() // this replaces the tfs object; it could static in the FileSystem class { static FileSystem fs; // define and initialize a local static object return fs; // return a reference to it } ``` . ``` class Directory { ... }; // as before Directory::Directory( params ) // as before, except references to tfs are now to tfs() { ... std::size_t disks = tfs().numDisks(); ... } Directory& tempDir() // this replaces the tempDir object; it could be static in the Directory class { static Directory td; // define/initialize local static object return td; // return reference to it } ``` Clients of this modified system program exactly as they used to, except they now refer to `tfs()` and `tempDir()` instead of `tfs` and `tempDir`. That is, they use functions returning references to objects instead of using the objects themselves. The reference-returning functions dictated by this scheme are always simple: define and initialize a local static object on line 1, return it on line 2. This simplicity makes them excellent candidates for inlining, especially if they're called frequently (see Item 30). On the other hand, the fact that these functions contain static objects makes them problematic in multithreaded systems. Then again, any kind of non-const static object — local or non-local — is trouble waiting to happen in the presence of multiple threads. One way to deal with such trouble is to manually invoke all the reference-returning functions during the single-threaded startup portion of the program. This eliminates initialization-related race conditions.

Original source