Does local static initialization without blocking exclude a possible deadlock in C ++ 11?

The document http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm presents an algorithm that should not contain a lock during initialization of a local static variable, but still cause a parallel thread control through variable definition to wait for initialization to complete.

The paper says this has the advantage of avoiding a possible deadlock.

The main problem with initializing an object with a local local static duration is that the containing function can be called at the same time, and therefore, the determination can be performed simultaneously. Failure to synchronize can lead to a race condition. The obvious solution is synchronization. The problem is that such synchronization can lead to a deadlock if the synchronization involves locking the lock while the initializer is running.

Can someone give an example that shows where the deadlock described above occurs?

+4
source share
2 answers

If you intend to hold a lock during local static initialization, two options are possible:

  • Select mutex on static.
  • Select only one mutex for all statics.

I am not 100% positive, but I believe that the quote you are referring to implicitly assumes design 2. And indeed, the algorithm presented in the article uses only one mutex for all the statics (called mu in the code).

In project 2, you get a dead end described by this answer . That is, if you are not actually storing the mutex during initialization. This is achieved by having a tri-state flag for each static element, which indicates one of: not initialized, initialized, already initialized. And use the global mutex to set the flag, but unlock it during initialization.

+4
source

This is a simple extension of the classic dead end to the case when one of the locks is provided by the compiler.

 void A2B() { a.Lock(); B(); a.Unlock(); } void B() { b.Lock(); ...; b.Unlock(); } void B2A() { b.Lock(); A(); b.Unlock(); } void A() { a.Lock(); ...; a.Unlock(); } 

A classic deadlock occurs if one thread calls A2B() and another thread calls B2A() .

In a static initialization lock, the compiler provides a lock b .

 int A() { a.Lock(); ...; a.Unlock(); return 0; } void B2A() { static int v = A(); } void A2B() { a.Lock(); B2A(); a.Unlock(); } 

If you assume a static initialization lock, then the code is secretly converted to

 void B2A() { if (!initialized) { b.Lock(); // standard double-check-lock if (!initialized) v = A(); initialized=true; b.Unlock(); } } 

One thread calls A2B() , and the rest calls B2A() .

+3
source

All Articles