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












6















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 << "Foon"; }
~Foo() { std::cout << "~Foon"; }
static Foo &instance();
};
struct Bar {
Bar() { std::cout << "Barn"; }
~Bar() { std::cout << "~Barn"; }
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.










share|improve this question























  • Couple of gratuitous opinions here: (1) You are going to a lot of trouble to understand tricky semantics of the language. I would spend the time trying to write code that Joe Average developer could read, understand, and safely modify without consulting the language spec. (2) You are going to a lot of trouble to understand static variables. I would spend the time trying to eliminate static variables from my design.

    – Solomon Slow
    Jan 2 at 15:59













  • @SolomonSlow I agree on both counts, and that is my long-term goal. However today I just want to fix a bug without refactoring the entire system.

    – Oktalist
    Jan 2 at 17:37
















6















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 << "Foon"; }
~Foo() { std::cout << "~Foon"; }
static Foo &instance();
};
struct Bar {
Bar() { std::cout << "Barn"; }
~Bar() { std::cout << "~Barn"; }
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.










share|improve this question























  • Couple of gratuitous opinions here: (1) You are going to a lot of trouble to understand tricky semantics of the language. I would spend the time trying to write code that Joe Average developer could read, understand, and safely modify without consulting the language spec. (2) You are going to a lot of trouble to understand static variables. I would spend the time trying to eliminate static variables from my design.

    – Solomon Slow
    Jan 2 at 15:59













  • @SolomonSlow I agree on both counts, and that is my long-term goal. However today I just want to fix a bug without refactoring the entire system.

    – Oktalist
    Jan 2 at 17:37














6












6








6


1






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 << "Foon"; }
~Foo() { std::cout << "~Foon"; }
static Foo &instance();
};
struct Bar {
Bar() { std::cout << "Barn"; }
~Bar() { std::cout << "~Barn"; }
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.










share|improve this question














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 << "Foon"; }
~Foo() { std::cout << "~Foon"; }
static Foo &instance();
};
struct Bar {
Bar() { std::cout << "Barn"; }
~Bar() { std::cout << "~Barn"; }
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.







c++ multithreading static c++14 static-order-fiasco






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 1 at 23:52









OktalistOktalist

10.4k12848




10.4k12848













  • Couple of gratuitous opinions here: (1) You are going to a lot of trouble to understand tricky semantics of the language. I would spend the time trying to write code that Joe Average developer could read, understand, and safely modify without consulting the language spec. (2) You are going to a lot of trouble to understand static variables. I would spend the time trying to eliminate static variables from my design.

    – Solomon Slow
    Jan 2 at 15:59













  • @SolomonSlow I agree on both counts, and that is my long-term goal. However today I just want to fix a bug without refactoring the entire system.

    – Oktalist
    Jan 2 at 17:37



















  • Couple of gratuitous opinions here: (1) You are going to a lot of trouble to understand tricky semantics of the language. I would spend the time trying to write code that Joe Average developer could read, understand, and safely modify without consulting the language spec. (2) You are going to a lot of trouble to understand static variables. I would spend the time trying to eliminate static variables from my design.

    – Solomon Slow
    Jan 2 at 15:59













  • @SolomonSlow I agree on both counts, and that is my long-term goal. However today I just want to fix a bug without refactoring the entire system.

    – Oktalist
    Jan 2 at 17:37

















Couple of gratuitous opinions here: (1) You are going to a lot of trouble to understand tricky semantics of the language. I would spend the time trying to write code that Joe Average developer could read, understand, and safely modify without consulting the language spec. (2) You are going to a lot of trouble to understand static variables. I would spend the time trying to eliminate static variables from my design.

– Solomon Slow
Jan 2 at 15:59







Couple of gratuitous opinions here: (1) You are going to a lot of trouble to understand tricky semantics of the language. I would spend the time trying to write code that Joe Average developer could read, understand, and safely modify without consulting the language spec. (2) You are going to a lot of trouble to understand static variables. I would spend the time trying to eliminate static variables from my design.

– Solomon Slow
Jan 2 at 15:59















@SolomonSlow I agree on both counts, and that is my long-term goal. However today I just want to fix a bug without refactoring the entire system.

– Oktalist
Jan 2 at 17:37





@SolomonSlow I agree on both counts, and that is my long-term goal. However today I just want to fix a bug without refactoring the entire system.

– Oktalist
Jan 2 at 17:37












1 Answer
1






active

oldest

votes


















5














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







share|improve this answer
























  • No guarantee means completely unsequenced? Not even indeterminately sequenced?

    – Oktalist
    Jan 2 at 2:44











  • @Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

    – llllllllll
    Jan 2 at 3:16











  • Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

    – amilamad
    Jan 3 at 3:55






  • 1





    @amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

    – llllllllll
    Jan 3 at 6:44











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999850%2forder-of-initialization-and-destruction-of-block-scope-static-vs-namespace-scop%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









5














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







share|improve this answer
























  • No guarantee means completely unsequenced? Not even indeterminately sequenced?

    – Oktalist
    Jan 2 at 2:44











  • @Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

    – llllllllll
    Jan 2 at 3:16











  • Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

    – amilamad
    Jan 3 at 3:55






  • 1





    @amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

    – llllllllll
    Jan 3 at 6:44
















5














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







share|improve this answer
























  • No guarantee means completely unsequenced? Not even indeterminately sequenced?

    – Oktalist
    Jan 2 at 2:44











  • @Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

    – llllllllll
    Jan 2 at 3:16











  • Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

    – amilamad
    Jan 3 at 3:55






  • 1





    @amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

    – llllllllll
    Jan 3 at 6:44














5












5








5







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







share|improve this answer













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








share|improve this answer












share|improve this answer



share|improve this answer










answered Jan 2 at 2:32









llllllllllllllllllll

13.8k41742




13.8k41742













  • No guarantee means completely unsequenced? Not even indeterminately sequenced?

    – Oktalist
    Jan 2 at 2:44











  • @Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

    – llllllllll
    Jan 2 at 3:16











  • Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

    – amilamad
    Jan 3 at 3:55






  • 1





    @amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

    – llllllllll
    Jan 3 at 6:44



















  • No guarantee means completely unsequenced? Not even indeterminately sequenced?

    – Oktalist
    Jan 2 at 2:44











  • @Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

    – llllllllll
    Jan 2 at 3:16











  • Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

    – amilamad
    Jan 3 at 3:55






  • 1





    @amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

    – llllllllll
    Jan 3 at 6:44

















No guarantee means completely unsequenced? Not even indeterminately sequenced?

– Oktalist
Jan 2 at 2:44





No guarantee means completely unsequenced? Not even indeterminately sequenced?

– Oktalist
Jan 2 at 2:44













@Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

– llllllllll
Jan 2 at 3:16





@Oktalist No. All implementation defined. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.

– llllllllll
Jan 2 at 3:16













Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

– amilamad
Jan 3 at 3:55





Can`t you use a one singleton object that includes both foo and bar objects?. I use this approach to ensure the correct initialization and destruction order.

– amilamad
Jan 3 at 3:55




1




1





@amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

– llllllllll
Jan 3 at 6:44





@amilamad foo is a thread_local, it can't be a non-static member of your singleton. If you meant a static member, then what is stated in this answer still applies.

– llllllllll
Jan 3 at 6:44




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999850%2forder-of-initialization-and-destruction-of-block-scope-static-vs-namespace-scop%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

MongoDB - Not Authorized To Execute Command

in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith

Npm cannot find a required file even through it is in the searched directory