Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Race conditions in static local variables

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.

like image 289
tina nyaa Avatar asked May 25 '14 13:05

tina nyaa


People also ask

How do you avoid race conditions in Labview?

A more robust way to prevent race conditions is to use a resource lock, or semaphore. The semaphore is a piece of code that gates access to a resource. When writing your code, you create the semaphore and pass its reference to each loop that may need to access the resource in question.

What are race conditions in C?

What are race conditions? Race conditions in software or any system occur when the desired output requires that certain events occur in a specific order but that the events don't always happen in that order. There is a 'race' between the events and if the wrong events win, the program fails.

How do you control race conditions?

To avoid race conditions, any operation on a shared resource – that is, on a resource that can be shared between threads – must be executed atomically. One way to achieve atomicity is by using critical sections — mutually exclusive parts of the program.


1 Answers

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.

like image 161
Mankarse Avatar answered Sep 24 '22 19:09

Mankarse