As mentioned there, Meyer's singleton is thread-safe in C++11.
So I would expect this code to be fine:
#include <stdio.h>
#include <pthread.h>
struct key_type {
int value;
key_type() : value(0) { }
};
void * thread1(void*) {
static key_type local_key;
printf("thread has key %d\n", local_key.value);
return NULL;
}
int main()
{
pthread_t t[2];
pthread_create(&t[0], NULL, thread1, NULL);
pthread_create(&t[1], NULL, thread1, NULL);
pthread_join(t[0], NULL);
pthread_join(t[1], NULL);
}
(code overly simplified on purpose, I know I can do zero-initialization trivially.)
I'm compiling with g++-7.1.0. Helgrind (valgrind-3.12.0) reports a possible data race between the read of local_key.value
and the ctor, which sets value
.
==29036== Possible data race during read of size 4 at 0x601058 by thread #3
==29036== Locks held: none
==29036== at 0x4006EA: thread1(void*) (datarace-simplest.cpp:12)
==29036== by 0x4C32D06: mythread_wrapper (hg_intercepts.c:389)
==29036== by 0x4E45493: start_thread (pthread_create.c:333)
==29036== by 0x59DEAFE: clone (clone.S:97)
==29036==
==29036== This conflicts with a previous write of size 4 by thread #2
==29036== Locks held: none
==29036== at 0x400780: key_type::key_type() (datarace-simplest.cpp:6)
==29036== by 0x4006DF: thread1(void*) (datarace-simplest.cpp:11)
==29036== by 0x4C32D06: mythread_wrapper (hg_intercepts.c:389)
==29036== by 0x4E45493: start_thread (pthread_create.c:333)
==29036== by 0x59DEAFE: clone (clone.S:97)
==29036== Address 0x601058 is 0 bytes inside data symbol "_ZZ7thread1PvE9local_key"
I thought that the c++11 standard (§6.7) guaranteed that local_key
was initialized just once and for all, so that further accesses dealt with variables whose ctor was guaranteed not to be still running.
Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. [...] If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. [...]
Am I wrong ? Is it a helgrind defect ? Is this use case known to slip through the cracks so that helgrind reports a possible race ?