为什么基于 Valgrind 的 Helgrind 线程错误检测器的 3.22.0 版本会报告数据争用,而 3.18.1 版本却不会?

问题描述 投票:0回答:1

1.背景

以下来自 cppreference.com 的 C++ 代码说明了如何将

std::condition_variable
std::mutex
结合使用以促进线程间通信。

#include <condition_variable>                                            //01
#include <iostream>                                                      //02
#include <mutex>                                                         //03
#include <string>                                                        //04
#include <thread>                                                        //05
                                                                         //06
std::mutex m;                                                            //07
std::condition_variable cv;                                              //08
std::string data;                                                        //09
bool ready = false;                                                      //10
bool processed = false;                                                  //11
                                                                         //12
void worker_thread() {                                                   //13
  // wait until main() sends data                                        //14
  std::unique_lock<std::mutex> lk(m);                                    //15
  cv.wait(lk, [] { return ready; });                                     //16
                                                                         //17
  // after the wait, we own the lock                                     //18
  std::cout << "Worker thread is processing data\n";                     //19
  data += " after processing";                                           //20
                                                                         //21
  // send data back to main()                                            //22
  processed = true;                                                      //23
  std::cout << "Worker thread signals data processing completed\n";      //24
                                                                         //25
  // manual unlocking is done before notifying, to avoid waking up       //26 
  // the waiting thread only to block again (see notify_one for details) //27
  lk.unlock();                                                           //28
  cv.notify_one();                                                       //29
}                                                                        //30
                                                                         //31
int main() {                                                             //32
  std::thread worker(worker_thread);                                     //33
                                                                         //34
  data = "Example data";                                                 //35
  // send data to the worker thread                                      //36
  {                                                                      //37
    std::lock_guard<std::mutex> lk(m);                                   //38
    ready = true;                                                        //39
    std::cout << "main() signals data ready for processing\n";           //40
  }                                                                      //41
  cv.notify_one();                                                       //42
                                                                         //43
  // wait for the worker                                                 //44
  {                                                                      //42
    std::unique_lock<std::mutex> lk(m);                                  //46
    cv.wait(lk, [] { return processed; });                               //47
  }                                                                      //48
  std::cout << "Back in main(), data = " << data << '\n';                //49
                                                                         //50
  worker.join();                                                         //51
}                                                                        //52

2.观察

以下观察假设上述源代码存储在名为

a.cpp
的文件中。这些观察是使用 WSL2 Ubuntu 22.03.4(在 MS Windows 10 上运行)进行的。

2.1.LLVM Clang TSan(线程清理器)版本 14.0

执行检测代码时不会报告数据争用/争用条件:

$ clang++ -fsanitize=thread -g -O1 -Wall a.cpp; ./a.out

main() signals data ready for processing
Worker thread is processing data
Worker thread signals data processing completed
Back in main(), data = Example data after processing

2.2.Valgrind DRD(数据竞争检测器)工具版本 3.18.1 和 3.22.0

  • 报告了2个错误:
$ clang++ -g -Wall a.cpp; valgrind --tool=drd ./a.out

drd, a thread error detector
Copyright (C) 2006-2020, and GNU GPL'd, by Bart Van Assche.
Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
Command: ./a.out

main() signals data ready for processing
 
  Probably a race condition: condition variable 0x10f118 has been signaled
  but the associated mutex 0x10f0f0 is not locked by the signalling thread.
  ┌────────────┬────────────────────────────────┬────────────────────────┬────┐
  │at 0x4E244C5│pthread_cond_signal_intercept   │drd_pthread_intercepts.c│1248│
  │by 0x4E244C5│pthread_cond_signal@*           │drd_pthread_intercepts.c│1261│
  │by 0x10A4D8 │main                            │a.cpp                   │  42│
  └────────────┴────────────────────────────────┴────────────────────────┴────┘
  cond 0x10f118 was first observed at:

  【TABLE 1】
  ┌────────────┬────────────────────────────────┬────────────────────────┬────┐
  │at 0x4E217F0│pthread_cond_wait_intercept     │drd_pthread_intercepts.c│1162│
  │by 0x4E217F0│pthread_cond_wait@*             │drd_pthread_intercepts.c│1170│
  │by 0x10A44C │void std                        │condition_variable      │ 103│
  │            │::condition_variable            │                        │    │
  │            │::wait<worker_thread()          │                        │    │
  │            │::$_0>(                         │                        │    │
  │            │  std::unique_lock<std::mutex>&,│                        │    │
  │            │  worker_thread()::$_0          │                        │    │
  │            │)                               │                        │    │
  │by 0x10A36B │worker_thread()                 │a.cpp                   │  16│
  │...         │                                │                        │    │
  └────────────┴────────────────────────────────┴────────────────────────┴────┘
 
   mutex 0x10f0f0 was first observed at:

  【TABLE 2】
  ┌────────────┬────────────────────────────────┬────────────────────────┬────┐
  │at 0x4E1B640│pthread_mutex_lock_intercept    │drd_pthread_intercepts.c│ 942│
  │by 0x4E1B640│pthread_mutex_lock@*            │drd_pthread_intercepts.c│ 955│
  │by 0x10A642 │__gthread_mutex_lock(           │gthr-default.h          │ 749│
  │            │  pthread_mutex_t*              │                        │    │
  │            │)                               │                        │    │
  │by 0x10A9C4 │std::mutex::lock()              │std_mutex.h             │ 100│
  │by 0x10AA6B │std::unique_lock<std::mutex>    │unique_lock.h           │ 139│
  │            │   ::lock()                     │                        │    │
  │by 0x10A720 │std::unique_lock<std::mutex>    │unique_lock.h           │  69│
  │            │   ::unique_lock(std::mutex&)   │                        │    │
  │by 0x10A35B │worker_thread()                 │a.cpp                   │  15│
  │...         │                                │                        │    │
  └────────────┴────────────────────────────────┴────────────────────────┴────┘

Worker thread is processing data
Worker thread signals data processing completed

  Thread 2:
  Probably a race condition: condition variable 0x10f118 has been signaled
  but the associated mutex 0x10f0f0 is not locked by the signalling thread.
  ┌────────────┬────────────────────────────────┬────────────────────────┬────┐
  │at 0x4E244C5│pthread_cond_signal_intercept   │drd_pthread_intercepts.c│1248│
  │by 0x4E244C5│pthread_cond_signal@*           │drd_pthread_intercepts.c│1261│
  │by 0x10A3D9 │worker_thread()                 │a.cpp                   │  29│
  │...         │                                │                        │    │
  └────────────┴────────────────────────────────┴────────────────────────┴────┘

  cond 0x10f118 was first observed at: See 【TABLE 1】

  mutex 0x10f0f0 was first observed at: See 【TABLE 2】

Back in main(), data = Example data after processing

  For lists of detected and suppressed errors, rerun with: -s
  ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 34 from 16)
  • 如果进行以下更改,使

    m
    互斥量在cv.notify_one()函数调用之前
    未解锁:

    • 第 28 行已被注释掉。

    • 42 号线与 41 号线交换。

  • 这两个代码更改基于以下参考:

    • 回答来自Wandering Logic
      的问题
      此代码中是否存在数据竞争?. Github
    • isocpp/CppCoreGuidelines
    • 项目中的Issue
2.3.Valgrind

Helgrind(螺纹错误检测器)工具版本 3.18.1 和 3.22.0

    报告了 19 个错误:
$ clang++ -g -Wall a.cpp; valgrind --tool=helgrind ./a.out Helgrind, a thread error detector Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al. Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info Command: ./a.out ---Thread-Announcement------------------------------------------ Thread #1 is the program's root thread ---Thread-Announcement------------------------------------------ Thread #2 was created at 0x53729F3: clone (clone.S:76) by 0x53738EE: __clone_internal (clone-internal.c:83) by 0x52E16D8: create_thread (pthread_create.c:295) by 0x52E21FF: pthread_create@@GLIBC_2.34 (pthread_create.c:828) by 0x4E13585: pthread_create_WRK (hg_intercepts.c:445) by 0x4E14A8C: pthread_create@* (hg_intercepts.c:478) by 0x50FD328: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30) by 0x10A849: std::thread::thread<void (&)(), , void>(void (&)()) (std_thread.h:143) by 0x10A477: main (a.cpp:33) ---------------------------------------------------------------- Possible data race during read of size 4 at 0x10F0F8 by thread #1 Locks held: none at 0x52E4F4A: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:94) by 0x4E1009A: mutex_lock_WRK (hg_intercepts.c:937) by 0x4E14E73: pthread_mutex_lock (hg_intercepts.c:960) by 0x10A642: __gthread_mutex_lock(pthread_mutex_t*) (gthr-default.h:749) by 0x10A9C4: std::mutex::lock() (std_mutex.h:100) by 0x10A8C2: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (std_mutex.h:229) by 0x10A49F: main (a.cpp:38) This conflicts with a previous write of size 4 by thread #2 Locks held: none at 0x52E696C: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E08AD: __pthread_cond_wait_common (pthread_cond_wait.c:419) by 0x52E08AD: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) Address 0x10f0f8 is 8 bytes inside data symbol "m" ---------------------------------------------------------------- Possible data race during write of size 4 at 0x10F0F8 by thread #1 Locks held: none at 0x52E4F5D: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:170) by 0x4E1009A: mutex_lock_WRK (hg_intercepts.c:937) by 0x4E14E73: pthread_mutex_lock (hg_intercepts.c:960) by 0x10A642: __gthread_mutex_lock(pthread_mutex_t*) (gthr-default.h:749) by 0x10A9C4: std::mutex::lock() (std_mutex.h:100) by 0x10A8C2: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (std_mutex.h:229) by 0x10A49F: main (a.cpp:38) This conflicts with a previous write of size 4 by thread #2 Locks held: none at 0x52E696C: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E08AD: __pthread_cond_wait_common (pthread_cond_wait.c:419) by 0x52E08AD: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) Address 0x10f0f8 is 8 bytes inside data symbol "m" ---------------------------------------------------------------- Possible data race during read of size 4 at 0x10F0FC by thread #1 Locks held: none at 0x52E4F60: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:172) by 0x4E1009A: mutex_lock_WRK (hg_intercepts.c:937) by 0x4E14E73: pthread_mutex_lock (hg_intercepts.c:960) by 0x10A642: __gthread_mutex_lock(pthread_mutex_t*) (gthr-default.h:749) by 0x10A9C4: std::mutex::lock() (std_mutex.h:100) by 0x10A8C2: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (std_mutex.h:229) by 0x10A49F: main (a.cpp:38) This conflicts with a previous write of size 4 by thread #2 Locks held: none at 0x52E4F60: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:172) by 0x4E1009A: mutex_lock_WRK (hg_intercepts.c:937) by 0x4E14E73: pthread_mutex_lock (hg_intercepts.c:960) by 0x10A642: __gthread_mutex_lock(pthread_mutex_t*) (gthr-default.h:749) by 0x10A9C4: std::mutex::lock() (std_mutex.h:100) by 0x10AA6B: std::unique_lock<std::mutex>::lock() (unique_lock.h:139) by 0x10A720: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (unique_lock.h:69) by 0x10A35B: worker_thread() (a.cpp:15) Address 0x10f0fc is 12 bytes inside data symbol "m" ---------------------------------------------------------------- Possible data race during write of size 4 at 0x10F0FC by thread #1 Locks held: none at 0x52E4F60: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:172) by 0x4E1009A: mutex_lock_WRK (hg_intercepts.c:937) by 0x4E14E73: pthread_mutex_lock (hg_intercepts.c:960) by 0x10A642: __gthread_mutex_lock(pthread_mutex_t*) (gthr-default.h:749) by 0x10A9C4: std::mutex::lock() (std_mutex.h:100) by 0x10A8C2: std::lock_guard<std::mutex>::lock_guard(std::mutex&) (std_mutex.h:229) by 0x10A49F: main (a.cpp:38) This conflicts with a previous write of size 4 by thread #2 Locks held: none at 0x52E4F60: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:172) by 0x4E1009A: mutex_lock_WRK (hg_intercepts.c:937) by 0x4E14E73: pthread_mutex_lock (hg_intercepts.c:960) by 0x10A642: __gthread_mutex_lock(pthread_mutex_t*) (gthr-default.h:749) by 0x10A9C4: std::mutex::lock() (std_mutex.h:100) by 0x10AA6B: std::unique_lock<std::mutex>::lock() (unique_lock.h:139) by 0x10A720: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (unique_lock.h:69) by 0x10A35B: worker_thread() (a.cpp:15) Address 0x10f0fc is 12 bytes inside data symbol "m" main() signals data ready for processing ---------------------------------------------------------------- Possible data race during write of size 4 at 0x10F0F8 by thread #1 Locks held: none at 0x52E6A90: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E6A90: pthread_mutex_unlock@@GLIBC_2.2.5 (pthread_mutex_unlock.c:368) by 0x4E10869: mutex_unlock_WRK (hg_intercepts.c:1184) by 0x4E14E9F: pthread_mutex_unlock (hg_intercepts.c:1202) by 0x10A692: __gthread_mutex_unlock(pthread_mutex_t*) (gthr-default.h:779) by 0x10A9F4: std::mutex::unlock() (std_mutex.h:118) by 0x10A8E7: std::lock_guard<std::mutex>::~lock_guard() (std_mutex.h:235) by 0x10A4CC: main (a.cpp:42) This conflicts with a previous write of size 4 by thread #2 Locks held: none at 0x52E696C: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E08AD: __pthread_cond_wait_common (pthread_cond_wait.c:419) by 0x52E08AD: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) Address 0x10f0f8 is 8 bytes inside data symbol "m" ---------------------------------------------------------------- Thread #1: pthread_cond_{signal,broadcast}: dubious: associated lock is not held by any thread at 0x4E109E8: pthread_cond_signal_WRK (hg_intercepts.c:1567) by 0x4E14ED6: pthread_cond_signal@* (hg_intercepts.c:1588) by 0x10A4D8: main (a.cpp:43) ---------------------------------------------------------------- Possible data race during write of size 8 at 0x10F120 by thread #1 Locks held: none at 0x52E03F3: __atomic_wide_counter_add_relaxed (atomic_wide_counter.h:57) by 0x52E03F3: __condvar_add_g1_start_relaxed (pthread_cond_common.c:52) by 0x52E03F3: __condvar_quiesce_and_switch_g1 (pthread_cond_common.c:294) by 0x52E03F3: pthread_cond_signal@@GLIBC_2.3.2 (pthread_cond_signal.c:77) by 0x4E10A4A: pthread_cond_signal_WRK (hg_intercepts.c:1570) by 0x4E14ED6: pthread_cond_signal@* (hg_intercepts.c:1588) by 0x10A4D8: main (a.cpp:43) This conflicts with a previous read of size 8 by thread #2 Locks held: none at 0x52E09E4: __atomic_wide_counter_load_relaxed (atomic_wide_counter.h:30) by 0x52E09E4: __condvar_load_g1_start_relaxed (pthread_cond_common.c:46) by 0x52E09E4: __pthread_cond_wait_common (pthread_cond_wait.c:486) by 0x52E09E4: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) by 0x10AD44: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (std_thread.h:259) Address 0x10f120 is 8 bytes inside data symbol "cv" ---------------------------------------------------------------- Possible data race during write of size 4 at 0x10F0F8 by thread #1 Locks held: none at 0x52E696C: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E08AD: __pthread_cond_wait_common (pthread_cond_wait.c:419) by 0x52E08AD: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A60C: void std::condition_variable::wait<main::$_1>(std::unique_lock<std::mutex>&, main::$_1) (condition_variable:103) by 0x10A4FD: main (a.cpp:48) This conflicts with a previous write of size 4 by thread #2 Locks held: none at 0x52E696C: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E08AD: __pthread_cond_wait_common (pthread_cond_wait.c:419) by 0x52E08AD: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) Address 0x10f0f8 is 8 bytes inside data symbol "m" ---------------------------------------------------------------- Possible data race during read of size 8 at 0x10F120 by thread #2 Locks held: none at 0x52E0900: __atomic_wide_counter_load_relaxed (atomic_wide_counter.h:30) by 0x52E0900: __condvar_load_g1_start_relaxed (pthread_cond_common.c:46) by 0x52E0900: __pthread_cond_wait_common (pthread_cond_wait.c:539) by 0x52E0900: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) by 0x10AD44: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (std_thread.h:259) by 0x10AD14: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (std_thread.h:266) by 0x10AC78: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (std_thread.h:211) by 0x50FD252: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30) by 0x4E13779: mythread_wrapper (hg_intercepts.c:406) This conflicts with a previous write of size 8 by thread #1 Locks held: none at 0x52E03F3: __atomic_wide_counter_add_relaxed (atomic_wide_counter.h:57) by 0x52E03F3: __condvar_add_g1_start_relaxed (pthread_cond_common.c:52) by 0x52E03F3: __condvar_quiesce_and_switch_g1 (pthread_cond_common.c:294) by 0x52E03F3: pthread_cond_signal@@GLIBC_2.3.2 (pthread_cond_signal.c:77) by 0x4E10A4A: pthread_cond_signal_WRK (hg_intercepts.c:1570) by 0x4E14ED6: pthread_cond_signal@* (hg_intercepts.c:1588) by 0x10A4D8: main (a.cpp:43) Address 0x10f120 is 8 bytes inside data symbol "cv" ---------------------------------------------------------------- Possible data race during read of size 4 at 0x10F0F8 by thread #2 Locks held: none at 0x52E41DB: __pthread_mutex_cond_lock (pthread_mutex_lock.c:94) by 0x52E0933: __pthread_cond_wait_common (pthread_cond_wait.c:616) by 0x52E0933: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) by 0x10AD44: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (std_thread.h:259) by 0x10AD14: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (std_thread.h:266) by 0x10AC78: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (std_thread.h:211) by 0x50FD252: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30) This conflicts with a previous write of size 4 by thread #1 Locks held: none at 0x52E696C: __pthread_mutex_unlock_usercnt (pthread_mutex_unlock.c:62) by 0x52E08AD: __pthread_cond_wait_common (pthread_cond_wait.c:419) by 0x52E08AD: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A60C: void std::condition_variable::wait<main::$_1>(std::unique_lock<std::mutex>&, main::$_1) (condition_variable:103) by 0x10A4FD: main (a.cpp:48) Address 0x10f0f8 is 8 bytes inside data symbol "m" ---------------------------------------------------------------- Possible data race during write of size 4 at 0x10F0F8 by thread #2 Locks held: none at 0x52E41EE: __pthread_mutex_cond_lock (pthread_mutex_lock.c:170) by 0x52E0933: __pthread_cond_wait_common (pthread_cond_wait.c:616) by 0x52E0933: pthread_cond_wait@@GLIBC_2.3.2 (pthread_cond_wait.c:627) by 0x4E1390B: pthread_cond_wait_WRK (hg_intercepts.c:1291) by 0x4E14EAA: pthread_cond_wait@* (hg_intercepts.c:1318) by 0x10A44C: void std::condition_variable::wait<worker_thread()::$_0>(std::unique_lock<std::mutex>&, worker_thread()::$_0) (condition_variable:103) by 0x10A36B: worker_thread() (a.cpp:16) by 0x10ADD6: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (invoke.h:61) by 0x10AD6C: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (invoke.h:96) by 0x10AD44: void std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (std_thread.h:259) by 0x10AD14: std::thread::_Invoker<std::tuple<void (*)()> >::operator()() (std_thread.h:266) by 0x10AC78: std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run() (std_thread.h:211) by 0x50FD252: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30) This conflicts with a previous write of size 4 by thread #1 Locks held: none ... by 0x10A4FD: main (a.cpp:48) Address 0x10f0f8 is 8 bytes inside data symbol "m" Worker thread is processing data Worker thread signals data processing completed ---------------------------------------------------------------- Thread #2: pthread_cond_{signal,broadcast}: dubious: associated lock is not held by any thread ... by 0x10A3D9: worker_thread() (a.cpp:29) ---------------------------------------------------------------- Possible data race during write of size 8 at 0x10F120 by thread #2 Locks held: none ... by 0x10A3D9: worker_thread() (a.cpp:29) ... This conflicts with a previous read of size 8 by thread #1 Locks held: none ... by 0x10A4FD: main (a.cpp:48) Address 0x10f120 is 8 bytes inside data symbol "cv" ---------------------------------------------------------------- Possible data race during read of size 8 at 0x10F120 by thread #1 Locks held: none ... by 0x10A4FD: main (a.cpp:48) This conflicts with a previous write of size 8 by thread #2 Locks held: none ... Address 0x10f120 is 8 bytes inside data symbol "cv" Back in main(), data = Example data after processing Use --history-level=approx or =none to gain increased speed, at the cost of reduced accuracy of conflicting-access information For lists of detected and suppressed errors, rerun with: -s ERROR SUMMARY: 19 errors from 14 contexts (suppressed: 0 from 0)

  • 执行第 2.2 节中提到的两个代码更改后:

    • 所有错误在 Helgrind 3.18.1 版本中消失。

    • Helgrind 3.22.0 版本仍存在 18 个错误。

3.问题

如何解决 Helgrind 3.22.0 版本报告的 DRD 未报告的错误?

  • 是否需要进行额外的源代码更改以适应 Helgrind 内部业务逻辑?

  • 是否应该使用 C++ 线程 API 的不同实现来代替

    std::thread

    (即 POSIX 线程、
    Boost.Thread,...)?

  • Valgrind 命令是否应与

    --suppressions

     选项一起使用(以防它报告的 18 个错误是误报)?

  • 还有什么吗?

c++ multithreading valgrind condition-variable data-race
1个回答
0
投票
一些部分答案。

    不,你可能无法更改你的代码,至少如果你继续使用 std::thread 的话。问题出在 libstdc++ 中(也可能出在 libc++ 中)。
  1. Valgrind 仅处理 pthreads 和 Qt 线程。如果您使用 Boost 线程、std::thread 或 C thrd,它们都归结为 pthread。问题在于如何使用 pthread 条件变量。因此,您可能可以编写不会产生这些错误的 pthread 代码。
  2. 目前我看到两种解决方案:抑制或修改 Valgrind 以放宽这些检查。理想情况下,我希望两全其美,直接使用 pthreads 保留 C 代码的这些错误,但当它们来自 libstdc++ 和 libc++ 时将其过滤掉。
© www.soinside.com 2019 - 2024. All rights reserved.