Lothar
Lothar

Reputation: 880

When is initialization of global constants with external linkage safe from static initialization order fiasco?

Consider the following example:

As I read the iso-FAQ I would assume that this results in static initialization order fiasco. However the iso-FAQ notes

The static initialization order fiasco can also, in some cases, apply to built-in/intrinsic types.

What does that some cases mean? Under which conditions are we save and sound from SIOF for built-in/intrinsic types, in particular constants? Or must that Construct On First Use Idiom be used for all constants with external linkage?

Note: In the real code I can not change the definition of g_TRAGIC.

Upvotes: 17

Views: 418

Answers (2)

mksteve
mksteve

Reputation: 13085

Compilers can produce different sorts of code.

Static initialized data segment

The compiler emits into a data section a name and its initial value.

.data
   dw myData   6

This is initialized at compile time, and is safely defined throughout the life of the program

constructed data

Another alternative is for the compiler to reserve some space for the variable, and create an initializer/constructor for the data, and then call the constructor just before main. With the destructor (if required) being performed atexit.

 class CriticalSection {
      CRITICAL_SECTION m_myCS;
      public:
         CriticalSection() {
              InitializeCriticalSection( &m_myCS );
         }
         ~CriticalSection() {
              DeleteCriticalSection( & m_myCS );
         }
 } cs;

combined

Some data may be performed in both stages.

 struct Data {
     bool initialized;
     void *(*pMalloc)( size_t size );
 }  FixMalloc = { true, MyMalloc };

I have seen compilers (VS2013) produce code which initializes initialized to true in the static data, but creates a function to assign pMalloc to MyMalloc at runtime. (This was because there was not a known constant for MyMalloc.)

singleton method

 SomeClass * GetSomeClass()
 {
     static SomeClass cls;
     return &cls;
 }

This is order defined - when it gets called, but requires a fully C++11 compiler to be thread safe.

Summary

The guarantees are :-

  1. statics in the same compilation unit are initialized top-to-bottom.
  2. statics are initialized single threaded.
  3. singletons have defined order of construction. But not necessarily thread safe.

The guarantees are not :-

  1. All of an object is initialized at the same time.
  2. Static initialization has a working runtime.

Before main is called, both your statics and the C/C++ runtime are bootstrapping. The order of construction issues also occur between your code and the runtime, and so you may construct some items with complex constructors which rely on services which can't be available.

Upvotes: 1

Dmitrii Bulashevich
Dmitrii Bulashevich

Reputation: 94

Further reading of iso-FAQ give us an answer.

SIOF happens if you try to initialize builtin/intrinsic type by return value of constant function.

const int g_MAGIC = f(g_TRAGIC);

Upvotes: 0

Related Questions