Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Consider the following example:

  • tt.h declares a global constant with external linkages extern int g_TRAGIC;

  • tt.cpp defines g_TRAGIC as follows const int g_TRAGIC = 0xF001;

  • my.cpp wants to use it to define its own global constant const int g_MAGIC = g_TRAGIC;

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.

like image 903
Lothar Avatar asked Feb 25 '16 13:02

Lothar


1 Answers

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.

like image 125
mksteve Avatar answered Oct 01 '22 16:10

mksteve