I wrote the following toy example:
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;
}
According to the C++11 standard, this should not contain any data race (cf. this post).
However TSAN with gcc 4.9.2, reports a data race:
==================
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&)
==================
What is wrong here ?
- is TSan buggy ? (When I am using Clang's toolchain, I get no data race report)
- does GCC emit code which is not thread safe? (I am not using -fno-threadsafe-statics though)
- is my understanding of static locals incorrect?