这个简单的 c++ 代码中的数据竞争在哪里

Where is the data race in this simple c++ code

提问人:Özgür Murat Sağdıçoğlu 提问时间:7/27/2022 更新时间:8/6/2022 访问量:1377

问:

对于这个简单的代码,两者和清理程序都会产生类似的关于数据争用的警告。是虚惊一场吗?问题是什么?clang++g++

法典:

#include <thread>
struct A
{
    void operator()()
    {
    }
};

struct B
{
    void operator()()
    {
    }
};

int main(void)
{
    // callable objects are created and moved into thread
    std::thread t1(A{});
    std::thread t2(B{});
    t1.join();
    t2.join();
    return 0;
}

编译标志:

-pthread -O0 -g -fsanitize=thread -fsanitize=undefined

消毒剂输出:g++

==================
WARNING: ThreadSanitizer: data race (pid=80173)
  Write of size 8 at 0x7b0400000800 by thread T2:
    #0 pipe ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1726 (libtsan.so.0+0x3ea28)
    #1 __sanitizer::IsAccessibleMemoryRange(unsigned long, unsigned long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp:276 (libubsan.so.1+0x20102)
    #2 std::thread::_State_impl<std::thread::_Invoker<std::tuple<B> > >::~_State_impl() /usr/include/c++/11/bits/std_thread.h:201 (a.out+0x5191)
    #3 <null> <null> (libstdc++.so.6+0xdc2cb)

  Previous write of size 8 at 0x7b0400000800 by thread T1:
    #0 pipe ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1726 (libtsan.so.0+0x3ea28)
    #1 __sanitizer::IsAccessibleMemoryRange(unsigned long, unsigned long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp:276 (libubsan.so.1+0x20102)
    #2 std::thread::_State_impl<std::thread::_Invoker<std::tuple<A> > >::~_State_impl() /usr/include/c++/11/bits/std_thread.h:201 (a.out+0x53a5)
    #3 <null> <null> (libstdc++.so.6+0xdc2cb)

  Thread T2 (tid=80176, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xdc398)
    #2 main a.cpp:20 (a.out+0x3396)

  Thread T1 (tid=80175, finished) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xdc398)
    #2 main a.cpp:19 (a.out+0x3383)

SUMMARY: ThreadSanitizer: data race ../../../../src/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp:276 in __sanitizer::IsAccessibleMemoryRange(unsigned long, unsigned long)
==================
ThreadSanitizer: reported 1 warnings

注意:仅当线程和UB清理程序都启用时,才会发出此警告。

C++ 线程清理器 ubsan

评论

1赞 Caleth 7/27/2022
看起来 g++ 抱怨两个空范围重叠
1赞 Some programmer dude 7/27/2022
您使用什么版本的 Clang 和 GCC?
10赞 Quimby 7/27/2022
我的猜测是线程清理器通过 UB 清理器在检测代码中发现错误。
0赞 Richard Critten 7/27/2022
仅供参考 - 如果你传给 Clang 它不会抱怨。所以可能在 std 库代码中。-stdlib=libc++
2赞 Victor Gubin 7/27/2022
线程不共享任何数据,以及两个例程都是空的,例如不可变的 - 此代码没有任何数据争用,也不能有任何数据争用。警告来自您在此处链接的库san_interceptors_posix.cpp:1726

答:

12赞 user17732522 7/27/2022 #1

该程序格式良好。它没有任何数据争用或其他未定义的行为,也没有任何争用条件或未指定的行为(除了在线程创建失败时因未捕获的异常而中止的可能性)。

线程清理器根本无法很好地与未定义的行为清理器配合使用。我不确定它们是否可以一起使用。我以前在组合它们时遇到过这样的问题,因此建议不要这样做。

如果它们注定要很好地一起玩,那么这确实是一个错误。

评论

2赞 prapin 7/28/2022
我在 Xcode 方案设置 GUI 上进行了试用。Xcode 允许您选择“地址”和“未定义”行为清理器,或者“线程”和“未定义”行为清理器。但不能同时使用地址和线程清理程序。这似乎表明 OP 用例是合法的。
3赞 Nikita Kniazev 8/3/2022 #2

这个回购

非检测代码

ThreadSanitizer通常要求所有代码 编译方式为 .如果某些代码(例如 dynamic libraries)没有使用标志进行编译,可能会导致 false 正面的比赛报告、假阴性的比赛报告和/或遗漏的筹码 报告中的框架取决于非检测代码的性质。自 不产生误报报告必须 在程序中查看所有同步,部分同步 操作(即原子操作和线程安全的静态操作 initialization) 在编译过程中被截获(并且只能 在编译过程中被截获)。 叠 跟踪收集还依赖于编译器检测(展开 每个内存上的堆栈访问成本太高)。-fsanitize=threadThreadSanitizerThreadSanitizer

从此常见问题解答条目

常见问题

  • 问:我在 libstdc++ 中看到看起来像是虚假报告的内容 (libc++) 代码。

这可能发生在 c++11 模式下。一个这样的 这里讨论案例。 请注意,这还不是很重 在使用 C++11 线程的代码上进行了测试。解决方案是 重建 libstdc++ (libc++) with (this 可能很棘手,并且该过程会定期更改;联系我们获取 详细信息)。libstdc++ (libc++) 也有可能有一个错误,我们已经 以前至少见过一个这样的ThreadSanitizerThreadSanitizer