GCC 的 TSAN 报告了线程安全静态本地的数据争用
GCC's TSAN reports a data race with a thread safe static local
我写了下面的玩具例子:
std::map<char, size_t> getMap(const std::string& s)
{
std::map<char, size_t> map;
size_t i = 0;
for (const char * b = s.data(), *end = b + s.size(); b != end; ++b)
{
map[*b] = i++;
}
return map;
}
void check(const std::string& s)
{
//The creation of the map should be thread safe according to the C++11 rules.
static const auto map = getMap("12abcd12ef");
//Now we can read the map concurrently.
size_t n = 0;
for (const char* b = s.data(), *end = b + s.size(); b != end; ++b)
{
auto iter = map.find(*b);
if (iter != map.end())
{
n += iter->second;
}
}
std::cout << "check(" << s << ")=" << n << std::endl;
}
int main()
{
std::thread t1(check, "abc");
std::thread t2(check, "def");
t1.join();
t2.join();
return 0;
}
根据C++11标准,这不应该包含任何数据竞赛(参见这篇文章(。
然而,带有 gcc 4.9.2 的 TSAN 报告了数据竞争:
==================
WARNING: ThreadSanitizer: data race (pid=14054)
Read of size 8 at 0x7f409f5a3690 by thread T2:
#0 TestServer::check(std::string const&) <null>:0 (TestServer+0x0000000cc30a)
#1 std::thread::_Impl<std::_Bind_simple<void (*(char const*))(std::string const&)> >::_M_run() <null>:0 (TestServer+0x0000000cce37)
#2 execute_native_thread_routine ../../../../../gcc-4.9.2/libstdc++-v3/src/c++11/thread.cc:84 (libstdc++.so.6+0x0000000b5bdf)
Previous write of size 8 at 0x7f409f5a3690 by thread T1:
#0 TestServer::getMap(std::string const&) <null>:0 (TestServer+0x0000000cc032)
#1 TestServer::check(std::string const&) <null>:0 (TestServer+0x0000000cc5dd)
#2 std::thread::_Impl<std::_Bind_simple<void (*(char const*))(std::string const&)> >::_M_run() <null>:0 (TestServer+0x0000000cce37)
#3 execute_native_thread_routine ../../../../../gcc-4.9.2/libstdc++-v3/src/c++11/thread.cc:84 (libstdc++.so.6+0x0000000b5bdf)
Location is global 'TestServer::check(std::string const&)::map' of size 48 at 0x7f409f5a3680 (TestServer+0x00000062b690)
Thread T2 (tid=14075, running) created by main thread at:
#0 pthread_create ../../../../gcc-4.9.2/libsanitizer/tsan/tsan_interceptors.cc:877 (libtsan.so.0+0x000000047c03)
#1 __gthread_create /home/Guillaume/Compile/objdir/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:662 (libstdc++.so.6+0x0000000b5d00)
#2 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) ../../../../../gcc-4.9.2/libstdc++-v3/src/c++11/thread.cc:142 (libstdc++.so.6+0x0000000b5d00)
#3 TestServer::main() <null>:0 (TestServer+0x0000000ae914)
#4 StarQube::runSuite(char const*, void (*)()) <null>:0 (TestServer+0x0000000ce328)
#5 main <null>:0 (TestServer+0x0000000ae8bd)
Thread T1 (tid=14074, finished) created by main thread at:
#0 pthread_create ../../../../gcc-4.9.2/libsanitizer/tsan/tsan_interceptors.cc:877 (libtsan.so.0+0x000000047c03)
#1 __gthread_create /home/Guillaume/Compile/objdir/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:662 (libstdc++.so.6+0x0000000b5d00)
#2 std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) ../../../../../gcc-4.9.2/libstdc++-v3/src/c++11/thread.cc:142 (libstdc++.so.6+0x0000000b5d00)
#3 TestServer::main() <null>:0 (TestServer+0x0000000ae902)
#4 StarQube::runSuite(char const*, void (*)()) <null>:0 (TestServer+0x0000000ce328)
#5 main <null>:0 (TestServer+0x0000000ae8bd)
SUMMARY: ThreadSanitizer: data race ??:0 TestServer::check(std::string const&)
==================
这是怎么回事?
- 是越野车吗?(当我使用Clang的工具链时,我没有得到数据竞争报告(
- GCC 是否发出非线程安全的代码?(我没有使用 -fno-threadsafe-statics(
- 我对静态局部变量的理解不正确吗?
是越野车吗?(当我使用Clang的工具链时,我没有得到数据竞争报告( GCC 是否发出非线程安全的代码?(我没有使用-fno-threadsafe->statics( 我对静态局部变量的理解不正确吗?
我相信这是gcc
为tsan
目的生成代码的部分的错误。
我试试这个:
#include <thread>
#include <iostream>
#include <string>
std::string message()
{
static std::string msg("hi");
return msg;
}
int main()
{
std::thread t1([]() { std::cout << message() << "n"; });
std::thread t2([]() { std::cout << message() << "n"; });
t1.join();
t2.join();
}
如果看看clang
和gcc
生成的代码,都很好, 在这两种情况下,都会为初始化静态局部变量的路径调用 __cxa_guard_acquire
。但是,如果检查我们是否需要初始化msg
,我们会遇到问题。
代码如下所示
if (atomic_flag/*uint8_t*/) {
lock();
call_constructor_of_msg();
unlock();
}
在生成clang
callq __tsan_atomic8_load
的情况下,但在gcc
的情况下,它会产生callq __tsan_read1
.请注意,此调用注释实际内存操作,不自己做操作。
所以它在运行时tsan
运行时库认为一切都不好,我们有数据竞赛,我在这里报告问题:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68338
看起来它固定在主干中,但不是在 GCC - 5.2 的当前稳定版本中
相关文章:
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 并行块(线程清理器)之外的 OpenMP 中的争用条件;误报?
- 如何在C++中创建争用条件
- C++上的手动重置事件(来自 C#)实现:如何避免争用条件
- 作为随机数生成器的争用条件
- 设计低线程争用的多线程聊天服务器
- 智能指针析构函数争用条件
- 尽管互斥锁,线程中的争用条件
- ThreadSanitizer 报告的数据争用
- 在C++中递增和递减全局变量时的争用条件
- 线程清理器检测到数据争用,问题出在哪里?
- 为什么此代码不创建争用条件?
- 防止派生析构函数中的 vtable 数据争用
- __has_include() 和后续 #include 之间是否存在争用条件
- 用静态版本的QT编译时,文件对话框崩溃
- 一次加载整个缓存行以避免争用其中的多个元素
- 什么保证两个不相关的线程中的不同不相关对象没有(不可避免的)争用条件?
- 此工厂方法是否会导致争用条件?
- GCC 的 TSAN 报告了线程安全静态本地的数据争用
- 静态局部变量中的争用条件