How to avoid false positives with Helgrind? How to avoid false positives with Helgrind? multithreading multithreading

How to avoid false positives with Helgrind?


The problem is that Helgrind doesn't understand GCC's atomic builtins, so doesn't realise that they are race-free and impose ordering on the program.

There are ways to annotate your code to help Helgrind, see http://valgrind.org/docs/manual/hg-manual.html#hg-manual.effective-use (but I'm not sure how to use them here, I already tried what sbabbi shows and it only solves part of the problem).

I would avoid yielding in a busy loop anyway, it's a poor form of synchronization. It could be done with a condition variable like so:

#include <thread>#include <atomic>#include <iostream>#include <condition_variable>int main(){    bool isReady(false);    std::mutex mx;    std::condition_variable cv;    int i = 1;    std::thread t([&isReady, &i, &mx, &cv]()    {        i = 2;        std::unique_lock<std::mutex> lock(mx);        isReady = true;        cv.notify_one();    });    {        std::unique_lock<std::mutex> lock(mx);        cv.wait(lock, [&] { return isReady; });    }    i = 3;    t.join();    std::cout << i;    return 0;}


Valgrind cannot know that the while (!isReady) loop (along with the implicit memory_order_release and memory_order_consume flags on the store and load), implies that the statement i = 2 is dependency ordered before i = 3.

You have to explicitly state this invariant by using valgrind ANNOTATE_HAPPENS_BEFORE and ANNOTATE_HAPPENS_AFTER macros:

#include <valgrind/drd.h>#include <thread>#include <atomic>#include <iostream>int main(){    std::atomic<bool> isReady(false);    int i = 1;    std::thread t([&isReady, &i]()    {        i = 2;        ANNOTATE_HAPPENS_BEFORE(&isReady);        isReady = true;    });    while (!isReady)        std::this_thread::yield();    ANNOTATE_HAPPENS_AFTER(&isReady);    i = 3;    t.join();    std::cout << i;    return 0;}

Here we are saying that the line at ANNOTATE_HAPPENS_BEFORE always happens before the line at ANNOTATE_HAPPENS_AFTER, we know that due to inspection of the program logic, but valgrind cannot prove that for you.

This program produces:

valgrind --tool=helgrind ./a.out==714== Helgrind, a thread error detector==714== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al.==714== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info==714== Command: ./val==714== ==714== ---Thread-Announcement------------------------------------------==714== ==714== Thread #1 is the program's root thread==714== ==714== ---Thread-Announcement------------------------------------------==714== ==714== Thread #2 was created==714==    at 0x59E169E: clone (in /usr/lib/libc-2.23.so)==714==    by 0x4E421D9: create_thread (in /usr/lib/libpthread-2.23.so)==714==    by 0x4E43C42: pthread_create@@GLIBC_2.2.5 (in /usr/lib/libpthread-2.23.so)==714==    by 0x4C316F3: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)==714==    by 0x4C327D7: pthread_create@* (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)==714==    by 0x5113DB4: __gthread_create (gthr-default.h:662)==714==    by 0x5113DB4: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (thread.cc:163)==714==    by 0x40109C: std::thread::thread<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/ennio/val)==714==    by 0x400F55: main (in /home/ennio/val)==714== ==714== ----------------------------------------------------------------==714== ==714== Possible data race during read of size 1 at 0xFFF00061F by thread #1==714== Locks held: none==714==    at 0x401585: std::atomic<bool>::operator bool() const (in /home/ennio/val)==714==    by 0x400F61: main (in /home/ennio/val)==714== ==714== This conflicts with a previous write of size 1 by thread #2==714== Locks held: none==714==    at 0x4015D5: std::__atomic_base<bool>::operator=(bool) (in /home/ennio/val)==714==    by 0x401550: std::atomic<bool>::operator=(bool) (in /home/ennio/val)==714==    by 0x400F1B: main::{lambda()#1}::operator()() const (in /home/ennio/val)==714==    by 0x40146F: void std::_Bind_simple<main::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (in /home/ennio/val)==714==    by 0x40140C: std::_Bind_simple<main::{lambda()#1} ()>::operator()() (in /home/ennio/val)==714==    by 0x4013EB: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1} ()> >::_M_run() (in /home/ennio/val)==714==    by 0x5113A9E: execute_native_thread_routine (thread.cc:83)==714==    by 0x4C318E7: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)==714==  Address 0xfff00061f is on thread #1's stack==714==  in frame #1, created by main (???:)==714== 3==714== ==714== For counts of detected and suppressed errors, rerun with: -v==714== Use --history-level=approx or =none to gain increased speed, at==714== the cost of reduced accuracy of conflicting-access information==714== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

To remove the error on isReady itself, I assume a suppression file on __atomic_base<bool>::operator= would be sufficient.