2

I am trying to understand the behavior of local statics with gcc and c++ (pre c++11 and post). A lot of times, in a member function I find myself doing something like so:

struct Foo
{
    void foo() 
    {
        static const bool bar = someFunc();
        //etc
    }
};

For example where someFunc() is getenv("SOME_ENV_VAR"). In the above code, what are the rules governing bar? I believe, but do not have a reference, that gcc will compile a synchronization mechanism (not sure what) to protect the above local static from multiple threads. How do things change if it is no longer const? or if we make it thread local with __thread? And if foo() is not a member function?

Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
Palace Chan
  • 8,845
  • 11
  • 41
  • 93
  • Possible duplicate: http://stackoverflow.com/questions/2373859/c-static-const-and-initialization-is-there-a-fiasco – eladidan Feb 13 '13 at 19:32

1 Answers1

2

The rules are actually defined by C++ std and not gcc: http://cpp0x.centaur.ath.cx/stmt.dcl.html

I'd say that:

  • gcc will protect initialization but not usage of the static; the variable is initialized the first time block is entered
  • const does not affect it, as long as it's static storage
  • __thread vars are initialized using their own initialization and are not protected by any extra lock (as pointed in comments by jmetcalfe)
  • static should be at block scope, so any block will do, even just {} pair.
  • (by Aurelien, see comments) someFunc will be called only once

There're other issues like re-entering from recursion and no deadlocks allowed, it is all described in the link above. Also, C++ 03 and 11 std versions are a bit different on this topic.

Quote:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization

$6.7.4, C++11

which means that it is thread-safe - but only in the new standard. Anyway in gcc this is controlled by -f[no-]threadsafe-statics option.

I'm also interested in the downvote, I mean, if you think the link is wrong it would be nice to tell why, so that me and others know.

queen3
  • 15,333
  • 8
  • 64
  • 119
  • 1
    If you are down-voting this answer, please consider providing a comment describing why you've down-voted it or a better answer to the question. – Nathan S. Feb 13 '13 at 19:33
  • Thread local variables can't be initialised dynamically like this. If you are implementing lazy initialisation of the thread locals yourself, initialisation could be concurrent, so someFunc() would need to be threadsafe or protected with extra synchronisation – jmetcalfe Feb 13 '13 at 19:38
  • I added an update but I don't see your point. First, there're no thread locals in the code above. Then, of course someFunc has to be thread-safe, but the variable initialization's thread-safety is completely separate thing, according to gcc option and c++11 std. – queen3 Feb 13 '13 at 19:43
  • 1
    I agree, just making the point that in the structure the OP is using, __thread will not work, since you can't dynamically initialise a thread local, so asking about the behaviour of this code would be if __thread was used doesn't really make sense. – jmetcalfe Feb 13 '13 at 19:51
  • 1
    Excellent answer queen3. I would add, to make it more clear, that thanks to the static initialization lock, gcc guarantees that someFunc() will get called only once without additional effort from the developer. And any thread arriving while someFunc() is being run by the first thread will block until someFunc() returns. – Aurélien Feb 13 '13 at 19:53
  • @jmetcalfe: right, but simple initialization is allowed... and I wonder if "static __thread int x = 1234" will be protected by lock or not. – queen3 Feb 13 '13 at 19:58
  • @queen3: I hope not! Why would you want locking on something like that? **edit** - just checked - don't see any extra synchronisation. – jmetcalfe Feb 13 '13 at 20:00
  • OK, that's what anyone would expect. I added this to the answer for completeness. – queen3 Feb 13 '13 at 20:05
  • But I wonder if "static __thread" initialization happens when block is entered or at the startup. As far as I remember there're flags that control __thread initialization time. I would guess that "static __thread" and __thread are same things and have nothing to do with normal statics. – queen3 Feb 13 '13 at 20:07
  • __thread wont work with auto storage I believe, just global/static/extern. There's some info on initialisation here http://www.akkadia.org/drepper/tls.pdf - basically an initialisation image is set up at startup and allocated and copied on thread creation. – jmetcalfe Feb 13 '13 at 20:13
  • _"C++ 03 and 11 std versions are a bit different on this topic."_ They aren't with GCC, it does exactly the same thing for all `-std` options – Jonathan Wakely Feb 14 '13 at 00:29
  • This is a great answer. You say that gcc will protect initialization but not usage. How does it do that? Does it wrap the static const bool = someFunc() portion with locking but move it away from the assembly for the foo function so that code isn't hit on every foo call? – Palace Chan Feb 14 '13 at 06:14
  • @Palace see http://gcc.gnu.org/viewcvs/branches/gcc-4_7-branch/gcc/cp/decl.c?view=markup - comment at line 6749. It's effectively double-checked locking, so the overhead after initialisation is quite minimal. – jmetcalfe Feb 14 '13 at 08:17
  • Thread-safe static initialization in GCC conforms to the Itanium C++ ABI, see http://mentorembedded.github.com/cxx-abi/abi.html#once-ctor – Jonathan Wakely Feb 14 '13 at 19:04