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 << "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
add a comment |
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
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 understandstatic
variables. I would spend the time trying to eliminatestatic
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
add a comment |
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
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
c++ multithreading static c++14 static-order-fiasco
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 understandstatic
variables. I would spend the time trying to eliminatestatic
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
add a comment |
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 understandstatic
variables. I would spend the time trying to eliminatestatic
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
add a comment |
1 Answer
1
active
oldest
votes
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
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
@amilamadfoo
is athread_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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
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
@amilamadfoo
is athread_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
add a comment |
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
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
@amilamadfoo
is athread_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
add a comment |
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 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
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
@amilamadfoo
is athread_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
add a comment |
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
@amilamadfoo
is athread_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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 eliminatestatic
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