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
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).
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
$ 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 m
mutex is not unlocked before the cv.notify_one()
function call:
Line 28 is commented out.
Line 42 is swapped with 41.
These two code changes are based on following references:
Wandering Logic
to question Is there a data race in this code?.$ 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.
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 --suppressions
option (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.
Update: The example code on my systems (FreeBSD arm64 and amd64) generates 2 errors for both DRD and Helgrind. That's with Valgrind built from git VALGRIND_3_22_0-240-gcbc2b1d0e.