I have a code snippet from the course which is "Learn Multithreading with Modern C++" from udemy. (it is concurent queue demo example)
#include <queue>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <future>
#include <string>
using namespace std;
template <class T>
class concurrent_queue_cv {
std::mutex m;
std::queue<T> q;
std::condition_variable cv;
public:
concurrent_queue_cv() = default;
void push(T value) {
std::lock_guard<std::mutex> lg(m);
q.push(value);
cv.notify_one();
}
void pop(T& value) {
std::unique_lock<std::mutex> lg(m);
cv.wait(lg, [this] {return !q.empty();});
value = q.front();
q.pop();
}
};
concurrent_queue_cv<string> cq;
// Waiting thread
void reader() {
string sdata;
cout << "Reader calling pop..." << endl;
cq.pop(sdata); // Pop the data off the queue
cout << "Reader received data: " << sdata << endl;
}
// Modyifing thread
void writer() {
std::this_thread::sleep_for(2s); // Pretend to be busy...
cout << "Writer calling push..." << endl;
cq.push("Populated"); // Push the data onto the queue
cout << "Writer returned from push..." << endl;
}
int main() {
cout << "Starting reader" << endl;
auto r = async(std::launch::async, reader);
cout << "Starting writer" << endl;
auto w = async(std::launch::async, writer);
r.wait();
w.wait();
}
When I run this code with helgrind I got these.
==1072==
==1072== Possible data race during write of size 1 at 0x4E03F4E by thread #1
==1072== Locks held: none
==1072== at 0x48488A6: mempcpy (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1072== by 0x4B3B661: _IO_new_file_xsputn (fileops.c:1236)
==1072== by 0x4B3B661: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==1072== by 0x4B2F3F0: fwrite (iofwrite.c:39)
==1072== by 0x49E2823: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072== by 0x49E2BDB: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072== by 0x10BB05: main (in /home/user/concurentQueue)
==1072==
==1072== This conflicts with a previous write of size 1 by thread #2
==1072== Locks held: none
==1072== at 0x48488A6: mempcpy (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1072== by 0x4B3B661: _IO_new_file_xsputn (fileops.c:1236)
==1072== by 0x4B3B661: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==1072== by 0x4B2F3F0: fwrite (iofwrite.c:39)
==1072== by 0x49E2823: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072== by 0x49E2BDB: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072== by 0x10B8CC: reader() (in /home/user/concurentQueue)
==1072== by 0x1131E3: void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void (*&&)()) (in /home/user/concurentQueue)
==1072== by 0x113113: std::__invoke_result<void (*)()>::type std::__invoke<void (*)()>(void (*&&)()) (in /home/user/concurentQueue)
==1072== Address 0x4e03f4e is 14 bytes inside a block of size 1,024 alloc'd
==1072== at 0x483C893: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==1072== by 0x4B2DD33: _IO_file_doallocate (filedoalloc.c:101)
==1072== by 0x4B3DEFF: _IO_doallocbuf (genops.c:347)
==1072== by 0x4B3CF5F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:745)
==1072== by 0x4B3B6E4: _IO_new_file_xsputn (fileops.c:1244)
==1072== by 0x4B3B6E4: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==1072== by 0x4B2F3F0: fwrite (iofwrite.c:39)
==1072== by 0x49E2823: std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072== by 0x49E2BDB: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (in /lib/x86_64-linux-gnu/libstdc++.so.6.0.28)
==1072== by 0x10BAC5: main (in /home/user/concurentQueue)
==1072== Block was alloc'd by thread #1
==1072==
--1072--
--1072-- used_suppression: 58 helgrind-glibc2X-004 /usr/lib/x86_64-linux-gnu/valgrind/default.supp:949
--1072-- used_suppression: 12 helgrind-glibc2X-005 /usr/lib/x86_64-linux-gnu/valgrind/default.supp:963
==1072==
==1072== ERROR SUMMARY: 72 errors from 8 contexts (suppressed: 70 from 49)
If I am not wrong, this code snippet may have data race according to helgrind.
but when I compile this with clang using -fsanitize=thread then it does not show any potential data race.
clang++ -fsanitize=thread -g -O1 concurentQueue.cpp -lpthread -o concurentQueue
My question is that can we trust helgrind ? Or does the code snippet have data race ?
The issues are because of the couts. When cout lines are removed then helgrind will not give any negative result.