Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC's TSAN reports a data race with a thread safe static local

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?
like image 580
Arnaud Avatar asked Dec 13 '14 22:12

Arnaud


1 Answers

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?

I believe this is bug in gcc part that generate code for tsan purposes.

I try this:

#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();
}

If look at code generate by clang and gcc, all good, __cxa_guard_acquire is called in both cases for path that init static local variable. But in case of check that we need init msg or not we have problem.

The code looks like this

if (atomic_flag/*uint8_t*/) {
  lock();
  call_constructor_of_msg();
  unlock();
}

in case of clang callq __tsan_atomic8_load was generated, but in the case of gcc it generate callq __tsan_read1. Note that this calls annotate real memory operations, not do operations by itself.

so it at runtime tsan runtime library thinks that all bad, and we have data race, I report problem here:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68338

and looks like it fixed in trunk, but not in current stable release of gcc - 5.2

like image 180
fghj Avatar answered Nov 10 '22 00:11

fghj