如there所述,Meyer的单例在C ++ 11中是线程安全的。
因此,我希望这段代码可以:
#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);
}
(代码故意过分简化,我知道我可以轻易地进行零初始化。)
我正在使用g ++-7.1.0进行编译。 Helgrind(valgrind-3.12.0)报告在读取
local_key.value
和设置value
的ctor之间可能发生数据竞争。==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"
我以为c ++ 11标准(第6.7节)保证了
local_key
会被一劳永逸地初始化,以便进一步访问处理的ctor保证不会继续运行的变量。否则,将在第一次初始化此类变量
控制通过其声明;这样的变量被认为
在初始化完成后进行初始化。 [...]如果控件同时输入声明
变量正在初始化,并发执行应等待
完成初始化。 [...]
我错了吗 ?这是黑面缺陷吗?是否知道此用例可以穿越裂缝,以便Helgrind报告可能的种族?
最佳答案
分解函数thread1,我看到对__cxa_guard_acquire的调用
和__cxa_guard_release,我认为我们可以合理地假设是
保护构造函数。但是,此类呼叫不会被拦截
Helgrind认为,因此Helgrind不会进行任何同步。这是Valgrind / helgrind中的错误/弱点,值得在valgrind bugzilla上提交错误。但是请注意,快速阅读代码后,对__cxa_guard_acquire和__cxa_guard_release的调用似乎并不直接匹配一对锁定/解锁:看起来该代码可能只是调用了acquire,然后未调用release:
00x000000000040077e <+24>: mov $0x600d00,%edi
0x0000000000400783 <+29>: callq 0x400610 <__cxa_guard_acquire@plt>
0x0000000000400788 <+34>: test %eax,%eax
0x000000000040078a <+36>: setne %al
0x000000000040078d <+39>: test %al,%al
0x000000000040078f <+41>: je 0x4007a5 <thread1(void*)+63>
0x0000000000400791 <+43>: mov $0x600d08,%edi
0x0000000000400796 <+48>: callq 0x40082e <key_type::key_type()>
0x000000000040079b <+53>: mov $0x600d00,%edi
0x00000000004007a0 <+58>: callq 0x400650 <__cxa_guard_release@plt>
0x00000000004007a5 <+63>: mov 0x20055d(%rip),%eax # 0x600d08 <_ZZ7thread1PvE9local_key>
稍微调试之后,看似警卫队位于
local_key,并在构造对象后设置为1。
我不太清楚__cxa_guard_release必须做什么。我想需要更多阅读c ++运行时库代码,以了解如何(也许)被告知helgrind关于在那里发生的事情。
还要注意,valgrind drd工具同样遭受相同的错误/弱点。