简体   繁体   中英

Order of initialization and destruction of block-scope static vs. namespace-scope thread_local in main thread

I'm trying to understand the sequencing rules for initialization and destruction of namespace-scope and block-scope objects with static storage duration and thread-local storage duration in the context of the main thread. Consider these two classes:

struct Foo {
    Foo() { std::cout << "Foo\n"; }
    ~Foo() { std::cout << "~Foo\n"; }
    static Foo &instance();
};
struct Bar {
    Bar() { std::cout << "Bar\n"; }
    ~Bar() { std::cout << "~Bar\n"; }
    static Bar &instance();
};

They are identical except for the implementations of their static instance member functions:

thread_local Foo t_foo;
Foo &Foo::instance() { return t_foo; }

Bar &Bar::instance() { static Bar s_bar; return s_bar; }

Bar is a Meyers singleton, a block-scope object with static storage duration.

Foo 's instance is a namespace-scope object with thread-local storage duration.

Now the main function:

int main() {
    Bar::instance();
    Foo::instance();
}

Here's the output from GCC 8.1.0 and Clang 5.0.0:

Bar
Foo
~Foo
~Bar

Try it live: https://coliru.stacked-crooked.com/a/f83a9ec588aed921

I had expected that Foo would be constructed first, because it is at namespace scope. I suppose the implementation is permitted to defer the initialization until the first odr-use of the object. I didn't know it could be deferred until after the initialization of a block-scope static, but I can live with that.

Now I reverse the order of the function calls in main :

int main() {
    Foo::instance();
    Bar::instance();
}

And here's the output:

Foo
Bar
~Foo
~Bar

Now I've moved the first odr-use of the Foo instance to before the first call to Bar::instance , and the order of initialization is as I expected.

But I thought the objects should be destroyed in the reverse order of their initialization, which does not appear to be happening. What am I missing?

In relation to the initialization and destruction of objects of static and thread-local storage duration, cppreference and the standard say things like "when the program starts", "when the thread starts", "when the program ends", and "when the thread ends", but how do these concepts relate to each other in the context of the main thread? Or to be more precise, the first thread and the last thread?

In my "real" problem, Foo (the thread-local) is used by the logger, and the destructor of the base class of Bar uses the logger, so it's a static destruction order fiasco. Other threads are spawned, but Bar (the Meyers singleton) is constructed and destroyed in the main thread. If I could understand the sequencing rules, then I could try to solve the "real" problem without resorting to just trying things at random.

The standard guarantees that destruction of Foo (the thread local storage) is before Bar (the static storage):

[basic.start.term]/2

The completions of the destructors for all initialized objects with thread storage duration within that thread strongly happen before the initiation of the destructors of any object with static storage duration.

However, there is no guarantee about the construction order. The standard only says that the thread local should be constructed before its first odr-use:

[basic.stc.thread]/2

A variable with thread storage duration shall be initialized before its first odr-use

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM