1.Background
The following C++ code from cppreference.com illustrates how std::condition_variable is used in combination with a std::mutex to facilitate inter-thread communication.
#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.Observations
Following observations assume above source code is stored in a file named a.cpp. These observations were made using WSL2 Ubuntu 22.03.4 (running on MS Windows 10).
2.1.LLVM Clang TSan (Thread Sanitizer) Version 14.0
No data race / race condition is reported when the instrumented code is executed:
$ 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 (Data Race Detector) Tool Versions 3.18.1 and 3.22.0
- 2 errors are reported:
$ 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)
These errors disappear if following changes are made so that the
mmutex is not unlocked before thecv.notify_one()function call:Line 28 is commented out.
Line 42 is swapped with 41.
These two code changes are based on following references:
- Answer from
Wandering Logicto question Is there a data race in this code?. - Issue in Github isocpp/CppCoreGuidelines project.
- Answer from
2.3.Valgrind Helgrind (Thread Error Detector) Tool Versions 3.18.1 and 3.22.0
- 19 errors are reported:
$ 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)
Once the two code changes mentioned in section 2.2 are performed:
All errors disappear with version 3.18.1 of Helgrind.
18 errors remain with version 3.22.0 of Helgrind.
3.Question
How to resolve errors reported by version 3.22.0 of Helgrind that DRD does not report?
Shall additional source code changes be performed to accommodate with Helgrind internal business logic?
Shall a different implementation of the thread API for C++ be used instead of
std::thread(i.e. POSIX Threads, Boost.Thread, ...)?Shall Valgrind command used with
--suppressionsoption (in case the 18 errors it reports are false positives) ?Anything else ?
Some partial answers.
Also, for Helgrind the changes since 18.1 include
Most of them are related to libstdc++ using new pthread timed functions that Helgrind didn't handle previously.