GCC的TSAN使用线程安全的静态本地来报告数据竞争 [英] GCC's TSAN reports a data race with a thread safe static local

查看:242
本文介绍了GCC的TSAN使用线程安全的静态本地来报告数据竞争的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了以下玩具示例:

  std :: map< char,size_t> getMap(const std :: string& s)
{
std :: map< char,size_t>地图;
size_t i = 0;
for(const char * b = s.data(),* end = b + s.size(); b!= end; ++ b)
{
map [* b ] = i ++;
}
返回地图;
}

void check(const std :: string& s)
{
//根据C ++创建映射应该是线程安全的11条规则。
static const auto map = getMap(12abcd12ef);
//现在我们可以同时读取地图。
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();
返回0;
}

根据C ++ 11标准,它不应该包含任何数据竞争(参见这篇文章)。



然而TSAN使用gcc 4.9.2报告数据竞赛:

  === =============== 
警告:ThreadSanitizer:数据竞争(pid = 14054)
线程T2在0x7f409f5a3690处读取大小8:
#0 TestServer :: check(std :: string const&)< null>:0(TestServer + 0x0000000cc30a)
#1 std :: thread :: _ Impl< std :: _ Bind_simple< void(*(char const *) )(std :: string const& amp;)> > :: _ 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)

先前在0x7f409f5a3690处写入大小为8的线程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)

位置是全球性的'TestServer :: check(std :: string const&):: map'大小为48,位于0x7f409f5a3680(TestServer + 0x00000062b690)

由主线程创建的线程T2(tid = 14075,正在运行):
#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 (TestServer + 0x0000000ae914)
#4 StarQube :: runSui(.6 + 0x0000000b5d00)
#3 TestServer :: main()< null> te(char const *,void(*)())null:0(TestServer + 0x0000000ce328)
#5 main null:0(TestServer + 0x0000000ae8bd)

由主线程创建的线程T1(tid = 14074,已完成):
#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主要< null>:0(TestServer + 0x0000000ae8bd)

摘要:ThreadSanitizer:data race ??:0 TestServer :: check(st d :: string const&)
==================

这里出了什么问题?


  • 是TSan越野车吗? (当我使用Clang的工具链时,我没有收到数据竞争报告)

  • GCC是否会发出不是线程安全的代码? (我没有使用-fno-threadsafe-statics)

  • 是我对静态locals的理解不正确吗?

  • class =h2_lin>解决方案


    是TSan越野车吗? (当我使用Clang的工具链时,我没有得到数据竞争报告)
    GCC是否发出不是线程安全的代码? (我没有使用-fno-threadsafe-> statics)
    是我对静态本地化的理解不正确?


    I相信这是 gcc 部分中为 tsan 生成代码的错误。



    我试试这个:

      #include< thread> 
    #include< iostream>
    #include< string>

    std :: string message()
    {
    static std :: string msg(hi);
    返回信息;


    int main()
    {
    std :: thread t1([](){std :: cout<< message()< <\\\
    ;});
    std :: thread t2([](){std :: cout<< message()<<\\\
    ;});

    t1.join();
    t2.join();

    如果查看 clang gcc ,都是好的,
    __ cxa_guard_acquire 静态局部变量。但是如果检查我们需要init 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



    并且看起来像在trunk中修复的,但不是当前稳定版本的gcc - 5.2


    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?

    解决方案

    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

    这篇关于GCC的TSAN使用线程安全的静态本地来报告数据竞争的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆