问题
I'm getting what I think are false positives when using helgrind with C++11 futures and packaged tasks. The following is with gcc-6.3.0 and valgrind-3.12 on a CentOS6 system. I've tried to follow the advice in the documentation to provide annotations. Have I done something wrong? What should I do to avoid the false positives, or is there really a race?
drdws0134$ cat hthread.cpp
#include <valgrind/helgrind.h>
#define _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(addr) ANNOTATE_HAPPENS_BEFORE(addr)
#define _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(addr) ANNOTATE_HAPPENS_BEFORE(addr)
#define _GLIBCXX_EXTERN_TEMPLATE -1
#include "thread.cc"
drdws0134$ cat bleep.cpp
#include <valgrind/helgrind.h>
#define _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(addr) ANNOTATE_HAPPENS_BEFORE(addr)
#define _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(addr) ANNOTATE_HAPPENS_BEFORE(addr)
#define _GLIBCXX_EXTERN_TEMPLATE -1
#include <future>
#include <iostream>
std::packaged_task<int()> pt;
void call_pt(){
pt();
}
int main(int, char **){
pt = std::packaged_task<int()>([](){ return 91; });
auto fut = pt.get_future();
std::thread t(call_pt);
std::cout << fut.get() << "\n";
t.join();
return 0;
}
drdws0134$ ./x make bleep
g++ -I/proj/desres/root/CentOS6/x86_64/valgrind/3.12.0-01/include -std=c++14 -pthread -ggdb -O0 -c -o hthread.o hthread.cpp
g++ -I/proj/desres/root/CentOS6/x86_64/valgrind/3.12.0-01/include -std=c++14 -pthread -ggdb -O0 -pthread bleep.cpp hthread.o -o bleep
drdws0134$ ./bleep
91
drdws0134$ which valgrind
valgrind is /proj/desres/root/CentOS6/x86_64/valgrind/3.12.0-01/bin/valgrind
valgrind is /usr/bin/valgrind
drdws0134$ valgrind --tool=helgrind ./bleep
==11476== Helgrind, a thread error detector
==11476== Copyright (C) 2007-2015, and GNU GPL'd, by OpenWorks LLP et al.
==11476== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==11476== Command: ./bleep
==11476==
==11476== ---Thread-Announcement------------------------------------------
==11476==
==11476== Thread #1 is the program's root thread
==11476==
==11476== ---Thread-Announcement------------------------------------------
==11476==
==11476== Thread #2 was created
==11476== at 0x321B8E8A6E: clone (in /lib64/libc-2.12.so)
==11476== by 0x321C00690F: do_clone.clone.0 (in /lib64/libpthread-2.12.so)
==11476== by 0x321C006E6C: pthread_create@@GLIBC_2.2.5 (in /lib64/libpthread-2.12.so)
==11476== by 0x4A0C553: pthread_create_WRK (hg_intercepts.c:427)
==11476== by 0x4A0D637: pthread_create@* (hg_intercepts.c:460)
==11476== by 0x407897: __gthread_create(unsigned long*, void* (*)(void*), void*) (gthr-default.h:662)
==11476== by 0x407C0F: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (thread.cc:163)
==11476== by 0x4056C0: std::thread::thread<void (&)()>(void (&)()) (thread:136)
==11476== by 0x402820: main (bleep.cpp:18)
==11476==
==11476== ----------------------------------------------------------------
==11476==
==11476== Possible data race during read of size 8 at 0x51F0C98 by thread #1
==11476== Locks held: none
==11476== at 0x405AF8: std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::get() const (unique_ptr.h:308)
==11476== by 0x404CF3: std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::operator*() const (unique_ptr.h:294)
==11476== by 0x404538: std::__future_base::_State_baseV2::wait() (future:329)
==11476== by 0x4064F6: std::__basic_future<int>::_M_get_result() const (future:687)
==11476== by 0x405729: std::future<int>::get() (future:766)
==11476== by 0x40282C: main (bleep.cpp:19)
==11476==
==11476== This conflicts with a previous write of size 8 by thread #2
==11476== Locks held: none
==11476== at 0x406EC6: std::enable_if<std::__and_<std::is_move_constructible<std::__future_base::_Result_base*>, std::is_move_assignable<std::__future_base::_Result_base*> >::value, void>::type std::swap<std::__future_base::_Result_base*>(std::__future_base::_Result_base*&, std::__future_base::_Result_base*&) (move.h:191)
==11476== by 0x406CC2: std::_Tuple_impl<0ul, std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>::_M_swap(std::_Tuple_impl<0ul, std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>&) (tuple:331)
==11476== by 0x406774: std::tuple<std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>::swap(std::tuple<std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>&) (tuple:1215)
==11476== by 0x405E68: void std::swap<std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>(std::tuple<std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>&, std::tuple<std::__future_base::_Result_base*, std::__future_base::_Result_base::_Deleter>&) (tuple:1548)
==11476== by 0x405224: std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::swap(std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>&) (unique_ptr.h:355)
==11476== by 0x404A26: std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) (future:538)
==11476== by 0x406746: void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (functional:227)
==11476== by 0x405B6B: std::result_of<void (std::__future_base::_State_baseV2::*&&(std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&))(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>::type std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (functional:251)
==11476== Address 0x51f0c98 is 24 bytes inside a block of size 64 alloc'd
==11476== at 0x4A07526: operator new(unsigned long) (vg_replace_malloc.c:334)
==11476== by 0x40313F: __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (new_allocator.h:104)
==11476== by 0x402FC4: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, (__gnu_cxx::_Lock_policy)2>&, unsigned long) (alloc_traits.h:416)
==11476== by 0x402DEF: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, (__gnu_cxx::_Lock_policy)2> > >(std::__allocated_ptr&) (allocated_ptr.h:103)
==11476== by 0x402C7D: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(std::_Sp_make_shared_tag, std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>*, main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr_base.h:613)
==11476== by 0x402BCE: std::__shared_ptr<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(std::_Sp_make_shared_tag, main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr_base.h:1100)
==11476== by 0x402AFF: std::shared_ptr<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()> >::shared_ptr<main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(std::_Sp_make_shared_tag, main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr.h:319)
==11476== by 0x402A5C: std::shared_ptr<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()> > std::allocate_shared<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr.h:620)
==11476== by 0x4029E5: std::shared_ptr<std::__future_base::_Task_state_base<int ()> > std::__create_task_state<int (), main::{lambda()#1}, std::allocator<int> >(main::{lambda()#1}&&, std::allocator<int> const&) (future:1451)
==11476== by 0x402969: std::packaged_task<int ()>::packaged_task<main::{lambda()#1}, std::allocator<int>, void>(std::allocator_arg_t, std::allocator<int> const&, main::{lambda()#1}&&) (future:1503)
==11476== by 0x4028FD: std::packaged_task<int ()>::packaged_task<main::{lambda()#1}, void>(main::{lambda()#1}&&) (future:1493)
==11476== by 0x4027E1: main (bleep.cpp:16)
==11476== Block was alloc'd by thread #1
==11476==
==11476== ----------------------------------------------------------------
==11476==
==11476== Possible data race during read of size 4 at 0x51F0D10 by thread #1
==11476== Locks held: none
==11476== at 0x40573A: std::future<int>::get() (future:766)
==11476== by 0x40282C: main (bleep.cpp:19)
==11476==
==11476== This conflicts with a previous write of size 4 by thread #2
==11476== Locks held: none
==11476== at 0x40761B: std::__future_base::_Result<int>::_M_set(int&&) (future:249)
==11476== by 0x403C70: std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<int>, std::__future_base::_Result_base::_Deleter>, std::_Bind_simple<std::reference_wrapper<main::{lambda()#1}> ()>, int>::operator()() const (future:1325)
==11476== by 0x403ACE: std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<int>, std::__future_base::_Result_base::_Deleter>, std::_Bind_simple<std::reference_wrapper<main::{lambda()#1}> ()>, int> >::_M_invoke(std::_Any_data const&) (functional:1717)
==11476== by 0x405264: std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>::operator()() const (functional:2127)
==11476== by 0x404A08: std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) (future:533)
==11476== by 0x406746: void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (functional:227)
==11476== by 0x405B6B: std::result_of<void (std::__future_base::_State_baseV2::*&&(std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&))(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>::type std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (functional:251)
==11476== by 0x404D7B: void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}::operator()() const (mutex:602)
==11476== Address 0x51f0d10 is 16 bytes inside a block of size 24 alloc'd
==11476== at 0x4A07526: operator new(unsigned long) (vg_replace_malloc.c:334)
==11476== by 0x4071D6: std::unique_ptr<std::__future_base::_Result<int>, std::__future_base::_Result_base::_Deleter> std::__future_base::_S_allocate_result<int, int>(std::allocator<int> const&) (future:294)
==11476== by 0x40713D: std::__future_base::_Task_state_base<int ()>::_Task_state_base<std::allocator<int> >(std::allocator<int> const&) (future:1373)
==11476== by 0x403280: std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>::_Task_state<{lambda()#1}>({lambda()#1}&&, main::{lambda()#1} const&) (future:1399)
==11476== by 0x4031F1: void __gnu_cxx::new_allocator<int>::construct<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, {lambda()#1}, main::{lambda()#1} const&>(std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>*, {lambda()#1}&&, main::{lambda()#1} const&) (new_allocator.h:120)
==11476== by 0x4030F7: void std::allocator_traits<std::allocator<int> >::construct<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, {lambda()#1}, std::allocator<int> const&>(std::allocator<int>&, std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>*, {lambda()#1}&&, std::allocator<int> const&) (alloc_traits.h:455)
==11476== by 0x402F31: std::_Sp_counted_ptr_inplace<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<{lambda()#1}, main::{lambda()#1} const&>(main::{lambda()#1}, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr_base.h:520)
==11476== by 0x402CF7: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(std::_Sp_make_shared_tag, std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>*, main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr_base.h:615)
==11476== by 0x402BCE: std::__shared_ptr<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(std::_Sp_make_shared_tag, main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr_base.h:1100)
==11476== by 0x402AFF: std::shared_ptr<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()> >::shared_ptr<main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(std::_Sp_make_shared_tag, main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr.h:319)
==11476== by 0x402A5C: std::shared_ptr<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()> > std::allocate_shared<std::__future_base::_Task_state<main::{lambda()#1}, std::allocator<int>, int ()>, main::{lambda()#1}, {lambda()#1}, main::{lambda()#1} const&>(main::{lambda()#1} const&, {lambda()#1}&&, main::{lambda()#1} const&) (shared_ptr.h:620)
==11476== by 0x4029E5: std::shared_ptr<std::__future_base::_Task_state_base<int ()> > std::__create_task_state<int (), main::{lambda()#1}, std::allocator<int> >(main::{lambda()#1}&&, std::allocator<int> const&) (future:1451)
==11476== Block was alloc'd by thread #1
==11476==
91
==11476==
==11476== For counts of detected and suppressed errors, rerun with: -v
==11476== Use --history-level=approx or =none to gain increased speed, at
==11476== the cost of reduced accuracy of conflicting-access information
==11476== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
drdws0134$
回答1:
Yes.
I just stumbled upon this problem and was about to write up a question almost identical to yours. I'm using g++/libstdc++ 6.2.0 and a development valgrind (>3.9.0)
The problem appears to be with libstdc++, not g++ specifically. Your code is correct. If you compile your sample code using clang++ and libc++ then helgrind (and drd) will not flag any races.
The output of:
g++ sample.cpp -pthread
and:
clang++ sample.cpp -std=c++11 -stdlib=libstdc++ -pthread
will both have races according to helgrind, but the output of:
clang++ sample.cpp -std=c++11 -stdlib=libc++ -pthread
does not (i.e. when using clang's own standard library)
来源:https://stackoverflow.com/questions/41926894/can-helgrind-valgrind-be-used-with-c11-futures