C++ Multithreading: is initialization of a local static lambda thread safe? [duplicate] C++ Multithreading: is initialization of a local static lambda thread safe? [duplicate] multithreading multithreading

C++ Multithreading: is initialization of a local static lambda thread safe? [duplicate]


The C++ standard guarantees that initialization of local statics, no matter how complex, is thread-safe, in that the initialization code will run exactly once, and no thread will run past the initialization code before initialization is complete.

Furthermore, it guarantees that invoking a std::function is a read operation from the view of thread safety, meaning that an arbitrary number of threads may do it at the same time, as long as the std::function object is not modified at the same time.

By these guarantees, and because you code does not contain anything else that accesses shared state, it should be thread-safe. If it still triggers TSan, there's a bug somewhere:

  • Most likely, GCC is using very tricky atomic code for the static initialization guarantee, which TSan cannot identify as safe. In other words, it's a bug in TSan. Make sure you are using the most up-to-date versions of both tools that you can. (Specifically, it appears that TSan is somehow missing some kind of barrier that makes sure that the initialization of the std::function is actually visible to other threads.)
  • Less likely, GCC's initialization magic is actually incorrect, and there is a real race condition here. I cannot find any bug report to the effect that 5.4 had such a bug that was later fixed. But you might want to try with a newer version of GCC anyway. (Latest is 6.3.)
  • Some people have suggested that the constructor of std::function may have a bug where it accesses global shared way in an unsafe way. But even if this is true, it should not matter, because your code should not invoke the constructor more than once.
  • There may be a bug in GCC in inlining a function containing statics into an OpenMP parallel loop. Perhaps that leads to duplication of the statics, or breaking of the safe initialization code. Inspection of the generated assembly would be necessary for this.

The first version of the code is different, by the way, because it is completely trivial. Under -O3, GCC will actually completely compute the loop at compile-time, effectively converting your main function to

std::cout << "Result is: " << 12522500 << std::endl;

https://godbolt.org/g/JDRPQV

And even if it didn't do that, there is no initialization done for the lambda (the variable is just a single byte of padding), thus no write accesses to anything, and no opportunity for data races.


The reasoning is wrong in both the answers posted so far.

It has nothing to do with lambda being function pointer. The reason is: if a function doesn't access unprotected shared data, then it is safe. In the case of auto computeSum= .. as defined in the question, which is simple, the ThreadSanitizer easily proves that it does not access any shared data. However, in case of std::function case, the code becomes a bit complex, and the sanitizer is either confused, or simply doesn't go to the extent to prove that it is still the same! It just gives up, seeing the std::function. Or it has bug — or worse, std::function is buggy!

Lets do this experiment: define int global = 100; at global namespace, and then do ++global; in the first lambda. See what the sanitizer says now. I believe it will give warning/error! That is enough to prove that it has nothing to do with lambda being function pointer as claimed by other answers.

As for your question:

Is initialization of a local static lambda thread safe?

Yes (since C++11). Please search this site for more detailed answers. This has been discussed many times.


Split the quest into two part:

First, is there a difference between lambda and closure during the static variable initialization ?

Yes, there is difference, but it doesn't matter, because gcc ensure the safety. and threadsanitizer may give warning because it isn't strong enough to analysis the std::function constructor.

The first one is lambda and the second one is closure.

The lambda contains a single field which is a function pointer and may not causing race problem because it is atomic.

But the closure is a function pointer with its enclosed variables, there are more than 1 fields in std::function, so can't be updated atomic.

Second, is there a difference between lambda and closure during the lambda/closure call ?

No, they are exactly the same. You function should protect the shared data used, no matter it is lambda or closure.