a simple case of a helgrind false negative?

David Yonge-Mallo <[email protected]> Mon, 4 Aug 2025 16:24:38 +0200
Newsgroups gmane.comp.debugging.valgrind
Message-ID <CAJjVr9Few5=VHn-hAh_nZO3GZWYdD-RsU3xd81ZDQ3Et03w4EA@mail.gmail.com>
--===============0166084763645450059==
Content-Type: multipart/alternative; boundary="000000000000d1a0aa063b8ade56"

--000000000000d1a0aa063b8ade56
Content-Type: text/plain; charset="UTF-8"

Hi, everyone,

I would like some help from helgrind experts to settle a disagreement with
a colleague.

We have the following code (very simplified):

--- begin example.cpp ---
#include <pthread.h>
#include <iostream>
#include <atomic>

bool flag = false;  // std::atomic<bool> flag{false};

void* callbackEvent(void* whatever) {
    flag = true;
    return whatever;
}

int main(void) {
    pthread_t callback_thread;

    pthread_create(&callback_thread, NULL, callbackEvent, NULL);

    bool copyOfFlag = flag;
    if (!copyOfFlag) {
        std::cout << "value of flag: " << copyOfFlag << std::endl;  // do
stuff assuming flag is false
    }

    pthread_join(callback_thread, NULL);
}
--- end example.cpp ---

> g++ -lpthread example.cpp -o example && valgrind --tool=helgrind
./example

Running helgrind naturally produces the output that there is a data race in
"flag" between the two threads.

If the flag is replaced by an atomic, helgrind produces a clean report.

According to my understanding, this is a false negative. There is still no
"happens-before" relationship between the read in thread #1 and the write
in thread #2. The program can terminate with copyOfFlag being either true
or false, which means the race condition still exists.

According to my colleague, helgrind by design only cares that a read and a
write on the same memory does not happen at the same time, and making the
bool atomic fixes this. The indeterminacy of the value of copyOfFlag is a
separate issue, which helgrind cannot detect as it is not within the scope
of its design.

I claimed that helgrind does not understand atomic variables. My colleague
disbelieved this and made the counterargument that if a mutex is used to
guard access to flag (instead of making it atomic), it also produces a
clean report, and since helgrind undoubtedly understands mutexes, it must
mean that the data race is fixed.

What is the correct view here?

Thank you for your time.

p.s. I am aware of bug 339330, but technically it only mentions false
positives and this is a case of (what I believe to be) a false negative.

-- 
David Yonge-Mallo

--000000000000d1a0aa063b8ade56
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div dir=3D"ltr"><br><div>Hi, everyone,</div><div><br></di=
v><div>I would like some help from helgrind experts to settle a disagreemen=
t with a colleague.<br><br>We have the following code (very simplified):</d=
iv><div><br>--- begin example.cpp ---</div><div>#include &lt;pthread.h&gt;<=
br>#include &lt;iostream&gt;<br>#include &lt;atomic&gt;<br><br>bool flag =
=3D false; =C2=A0// std::atomic&lt;bool&gt; flag{false};<br><br>void* callb=
ackEvent(void* whatever) {<br>=C2=A0 =C2=A0 flag =3D true;<br>=C2=A0 =C2=A0=
 return whatever;<br>}<br><br>int main(void) {<br>=C2=A0 =C2=A0 pthread_t c=
allback_thread;<br><br>=C2=A0 =C2=A0 pthread_create(&amp;callback_thread, N=
ULL, callbackEvent, NULL);<br><br>=C2=A0 =C2=A0 bool copyOfFlag =3D flag;<b=
r>=C2=A0 =C2=A0 if (!copyOfFlag) {<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 std::cout=
 &lt;&lt; &quot;value of flag: &quot; &lt;&lt; copyOfFlag &lt;&lt; std::end=
l;=C2=A0 // do stuff assuming flag is false<br>=C2=A0 =C2=A0 }<br><br>=C2=
=A0 =C2=A0 pthread_join(callback_thread, NULL);<br>}<br></div><div>--- end =
example.cpp ---<br><br>&gt; g++ -lpthread example.cpp -o example &amp;&amp;=
 valgrind --tool=3Dhelgrind ./example=C2=A0</div><div><br>Running helgrind =
naturally produces the output that there is a data race in &quot;flag&quot;=
 between the two threads.=C2=A0<br><br></div><div>If the flag is replaced b=
y an atomic, helgrind produces a clean report.</div><div><br></div><div>Acc=
ording to my understanding, this is a false negative. There is still no &qu=
ot;happens-before&quot; relationship between the read in thread #1 and the =
write in thread #2. The program can terminate with copyOfFlag being either =
true or false, which means the race condition still exists.</div><div><br><=
/div><div>According to my colleague, helgrind by design only cares that a r=
ead and a write on the same memory does not happen at the same time, and ma=
king the bool atomic fixes this. The indeterminacy of the value of copyOfFl=
ag is a separate issue, which helgrind cannot detect as it is not within th=
e scope of its design.</div><div><br></div><div>I claimed that helgrind doe=
s not understand atomic variables. My colleague disbelieved this and made t=
he counterargument that if a mutex is used to guard access to flag (instead=
 of making it atomic), it also produces a clean report, and since helgrind =
undoubtedly understands mutexes, it must mean that the data race is fixed.<=
/div><div><br></div><div>What is the correct view here?=C2=A0<br><br>Thank =
you for your time.<br><br>p.s. I am aware of bug=C2=A0339330, but technical=
ly it only mentions false positives and this is a case of (what I believe t=
o be) a false negative.<br><br>--=C2=A0<br>David Yonge-Mallo</div></div>
</div>

--000000000000d1a0aa063b8ade56--


--===============0166084763645450059==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--===============0166084763645450059==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Valgrind-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/valgrind-users

--===============0166084763645450059==--