Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double initialization of a static STL container in a C++ library

There are a few good questions and answers here around the "static initialization order fiasco", but I seem to have hit against yet another expression of it, specially ugly because it does not crash but looses and leaks data.

I have a custom C++ library and an application that links against it. There is an static STL container in the library that registers all instances of a class. Those instances happen to be static variables in the application.

As a result of the "fiasco" (I believe), we get the container filled with the application instances during application initialization, then the library gets to initialize and the container is reset (probably leaking memory), ending up only with the instances from the library.

This is how I reproduced it with simplified code:

mylib.hpp:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class MyLibClass {
    static vector<string> registry;
    string myname;
  public:
    MyLibClass(string name);
};

mylib.cpp:

#include "mylib.hpp"

vector<string> MyLibClass::registry;

MyLibClass::MyLibClass(string name)
: myname(name)
{
    registry.push_back(name);
    for(unsigned i=0; i<registry.size(); i++)
        cout << " ["<< i <<"]=" << registry[i];
    cout << endl;
}

MyLibClass l1("mylib1");
MyLibClass l2("mylib2");
MyLibClass l3("mylib3");

myapp.cpp:

#include "mylib.hpp"

MyLibClass a1("app1");
MyLibClass a2("app2");
MyLibClass a3("app3");

int main() {
    cout << "main():" << endl;
    MyLibClass m("main");
}

Compile the objects with:

g++ -Wall -c myapp.cpp mylib.cpp
g++ myapp.o mylib.o -o myapp1
g++ mylib.o myapp.o -o myapp2

Run myapp1:

$ ./myapp1
 [0]=mylib1
 [0]=mylib1 [1]=mylib2
 [0]=mylib1 [1]=mylib2 [2]=mylib3
 [0]=mylib1 [1]=mylib2 [2]=mylib3 [3]=app1
 [0]=mylib1 [1]=mylib2 [2]=mylib3 [3]=app1 [4]=app2
 [0]=mylib1 [1]=mylib2 [2]=mylib3 [3]=app1 [4]=app2 [5]=app3
main():
 [0]=mylib1 [1]=mylib2 [2]=mylib3 [3]=app1 [4]=app2 [5]=app3 [6]=main

Run myapp2:

$ ./myapp2
 [0]=app1
 [0]=app1 [1]=app2
 [0]=app1 [1]=app2 [2]=app3
 [0]=mylib1
 [0]=mylib1 [1]=mylib2
 [0]=mylib1 [1]=mylib2 [2]=mylib3
main():
 [0]=mylib1 [1]=mylib2 [2]=mylib3 [3]=main

Here comes the question, the static vector was re-initialized, or used before initialization? Is this an expected behavior?

If I 'ar' the library as 'mylib.a' (ar rcs mylib.a mylib.o), the problem does not happen, but probably because there is only one valid order to link to the .a and it is by having the library in the last place, as for myapp1 here.

But in our real application, a more complex one with many object files and a few static (.a) libraries sharing a few static registries, the problem is happening and the only way we managed to solve it so far is by applying '[10.15] How do I prevent the "static initialization order fiasco"?'.

(I am still researching in our somewhat complex build system to see if we are linking correctly).

like image 391
gatopeich Avatar asked Mar 24 '11 16:03

gatopeich


2 Answers

If you give vector<string> a custom constructor you will see, that it is indeed called only once, but in myapp2 you are using registry uninitialized first, then it gets initialized ("removing" everything that's inside) and then filled again. That it doesn't segfault is just luck :)

I can't tell which part of the standard says something about this behaviour, but IMHO you should /never/ let static variables depend on each other. You might use a Meyers singleton for example for registry.

like image 37
filmor Avatar answered Sep 27 '22 19:09

filmor


One way to work around initialization order problems is to move the static variables from global scope to local scope.

Instead of having a registry variable within the class, put it into a function:

vector<string> & MyLibClass::GetRegistry()
{
    static vector<string> registry;
    return registry;
}

In the places where you would have used registry directly, have it call GetRegistry.

like image 64
Mark Ransom Avatar answered Sep 27 '22 18:09

Mark Ransom