2 questions (below) about the C++11 static initialization at [1] in this reference code (this is a complete tested c++11 example program).
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
struct Foo {
/* complex member variables. */
};
void DoSomething(Foo *foo) {
// Complex, but signal safe, use of foo.
}
Foo InitFoo() {
Foo foo;
/* complex, but signal safe, initialization of foo */
return foo;
}
Foo* GetFoo() {
static Foo foo = InitFoo(); // [1]
return &foo;
}
void Handler(int sig) {
DoSomething(GetFoo());
}
int main() {
// [2]
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = Handler;
sigaction(SIGINT, &act, nullptr);
for (;;) {
sleep(1);
DoSomething(GetFoo());
}
}
Question1: Is this guaranteed safe (no deadlocks etc)? C++11 static initialization involves locks. What if the signal is delivered before/after/during the first call to GetFoo() in main?
Question2: Is this guaranteed safe if a call to GetFoo() is inserted at [2] before the signal handler is installed? (Edit:) I.e. does inserting GetFoo() at [2] ensure that, later, when a signal arrives while the loop is operating, that there will be no deadlock?
I'm assuming C++11 (g++ or clang) on recent GNU/Linux, although answers for various Unices would also be interesting. (Spoiler: I think the answer is 1:NO and 2:YES but I don't know how to prove it.)
Edit: To be clear, I can imagine static initialization could be implemeted like this:
Mutex mx; // global variable
bool done = false; // global variable
...
lock(mx);
if (!done) {
foo = InitFoo();
done = true;
}
unlock(mx);
and then it would not be deadlock safe because the signal handler might lock mx while the main thread has it locked.
But there are other implementations, for example:
Mutex mx; // global variable
std::atomic<bool> done = false; // global variable
...
if (!done.load()) {
lock(mx);
if (!done.load()) {
foo = InitFoo();
done.store(true);
}
unlock(mx);
}
which would not have potential for deadlock provided the codepath was run completely at least once before a signal handler runs it.
My question is whether the c++11 (or any later) standard requires the implementation to be async-signal-safe (deadlock free, aka lock free) after the initial pass through the code has completed?