Search code examples
c++concurrencyvalgrind

Helgrind is working correct or not for concurrent queue example?


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 ?


Solution

  • The issues are because of the couts. When cout lines are removed then helgrind will not give any negative result.