I came across some code that had a function similar to the following, using some templated class A
:
template<typename X>
A<X>* get_A() {
static char storage[sizeof(A<X>)];
static A<X>* ptr = nullptr;
if(!ptr) {
new (reinterpret_cast<A<X>*>(storage)) A<X>();
ptr = reinterpret_cast<A<X>*>(storage);
}
return ptr;
}
I needed to make this initialization thread safe, so I changed this to:
A<X>* get_A() {
static A<X> a;
return &a;
}
This however causes a segfault: get_A()
is used in the destructor of other static classes that are destructed later. The complicated construction of A
is apparently there to extend the lifetime of A
beyond the destruction of any other object, and is itself never destructed. I noticed that https://en.cppreference.com/w/cpp/utility/program/exit says
If a function-local (block-scope) static object was destroyed and then that function is called from the destructor of another static object and the control flow passes through the definition of that object (or if it is used indirectly, via pointer or reference), the behavior is undefined.
Since the static storage and ptr are not 'objects', I think this rule does not make the first version undefined behavior, but it does prevent constructing a static std::mutex
inside the function. My questions therefore are:
- According to the C++11 standard (or newer), given that
get_A
is called from the destructor of an object that has static lifetime, is the first version in a single-threaded program under all circumstances indeed legal or may it impose undefined behaviour? - How can I make this thread safe without invoking undefined behaviour and without having to change the use of
get_A
by other classes? I prefer not to have initializing code for every possibleX
that templateA
is instantiated with, becauseA
is instantiated with many different types. Unless that turns out to be the only good solution.