Текущее состояние поддержки drd и helgrind для std:: thread

При переходе моего кода на С++ 11, я бы очень хотел преобразовать свой код pthread в std:: thread. Тем не менее, я, кажется, получаю ложные условия гонки на очень простых программах в drd и helgrind.

#include <thread>

int main(int argc, char** argv)
{
    std::thread t( []() { } );
    t.join();
    return 0;
}

Отказоустойчивый вывод helgrind. Я также получаю подобные ошибки в drd, используя gcc 4.6.1, valgrind 3.7.0 на Ubuntu 11.11 amd64.

Мои вопросы:

  • Проверка здравомыслия: я делаю что-то неправильно? Другие получают похожие ложные отчеты о простых программах std:: thread?
  • Какие текущие пользователи используют std:: thread для определения условий гонки?

Я не хочу переносить тонну кода с pthread на std::thread до тех пор, пока не появятся некоторые важные инструменты, такие как helgrind/drd.

==19347== ---Thread-Announcement------------------------------------------
==19347== 
==19347== Thread #1 is the program root thread
==19347== 
==19347== ---Thread-Announcement------------------------------------------
==19347== 
==19347== Thread #2 was created
==19347==    at 0x564C85E: clone (clone.S:77)
==19347==    by 0x4E37E7F: do_clone.constprop.3 (createthread.c:75)
==19347==    by 0x4E39604: [email protected]@GLIBC_2.2.5 (createthread.c:256)
==19347==    by 0x4C2B3DA: pthread_create_WRK (hg_intercepts.c:255)
==19347==    by 0x4C2B55E: [email protected]* (hg_intercepts.c:286)
==19347==    by 0x50BED02: std::thread::_M_start_thread(std::shared_ptr<std::thread::_Impl_base>) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16)
==19347==    by 0x400D51: _ZNSt6threadC1IZ4mainEUlvE_IEEEOT_DpOT0_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400C60: main (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347== 
==19347== ----------------------------------------------------------------
==19347== 
==19347== Possible data race during write of size 8 at 0x5B8E060 by thread #1
==19347== Locks held: none
==19347==    at 0x40165E: _ZNSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEED1Ev (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401895: _ZNKSt19_Sp_destroy_inplaceINSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEEEclEPS6_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x4016D8: _ZNSt19_Sp_counted_deleterIPNSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEESt19_Sp_destroy_inplaceIS6_ESaIS6_ELN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401B83: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401B3E: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401A93: std::__shared_ptr<std::thread::_Impl_base, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401AAD: std::shared_ptr<std::thread::_Impl_base>::~shared_ptr() (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400D5D: _ZNSt6threadC1IZ4mainEUlvE_IEEEOT_DpOT0_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400C60: main (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347== 
==19347== This conflicts with a previous read of size 8 by thread #2
==19347== Locks held: none
==19347==    at 0x50BEABE: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.16)
==19347==    by 0x4C2B547: mythread_wrapper (hg_intercepts.c:219)
==19347==    by 0x4E38EFB: start_thread (pthread_create.c:304)
==19347==    by 0x564C89C: clone (clone.S:112)
==19347== 
==19347== Address 0x5B8E060 is 32 bytes inside a block of size 64 alloc'd
==19347==    at 0x4C29059: operator new(unsigned long) (vg_replace_malloc.c:287)
==19347==    by 0x4012E9: _ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceINSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEESaIS8_ELNS_12_Lock_policyE2EEE8allocateEmPKv (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x40117C: _ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE2EEC1INSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEESaISA_EIS9_EEESt19_Sp_make_shared_tagPT_RKT0_DpOT1_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x4010B9: _ZNSt12__shared_ptrINSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEELN9__gnu_cxx12_Lock_policyE2EEC1ISaIS6_EIS5_EEESt19_Sp_make_shared_tagRKT_DpOT0_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401063: _ZNSt10shared_ptrINSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEEEC1ISaIS6_EIS5_EEESt19_Sp_make_shared_tagRKT_DpOT0_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x401009: _ZSt15allocate_sharedINSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEESaIS6_EIS5_EESt10shared_ptrIT_ERKT0_DpOT1_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400EF7: _ZSt11make_sharedINSt6thread5_ImplISt12_Bind_resultIvFZ4mainEUlvE_vEEEEIS5_EESt10shared_ptrIT_EDpOT0_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400E17: _ZNSt6thread15_M_make_routineISt12_Bind_resultIvFZ4mainEUlvE_vEEEESt10shared_ptrINS_5_ImplIT_EEEOS7_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400D2B: _ZNSt6threadC1IZ4mainEUlvE_IEEEOT_DpOT0_ (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347==    by 0x400C60: main (in /mnt/home/kfeng/dev/robolab/cpp/sbx/sandbox)
==19347== 
==19347== ----------------------------------------------------------------
==19347==

Ответ 1

std:: thread использует общий указатель внутри. То, что вы видите, - это ложные срабатывания в счетчике ссылок этого общего объекта указателя. Вы можете избежать этих ложных срабатываний, добавив четыре строки кода, показанные ниже в каждом исходном файле, перед заголовком заголовка С++. Примечание: это работает только с версией libstdС++, включенной в gcc 4.6.0 или новее.

#include <valgrind/drd.h>
#define _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(addr) ANNOTATE_HAPPENS_BEFORE(addr)
#define _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(addr) ANNOTATE_HAPPENS_AFTER(addr)
#define _GLIBCXX_EXTERN_TEMPLATE -1

Дополнительные сведения см. также в разделе "Охота за гоночными данными" в руководстве libstdС++ (http://gcc.gnu.org/onlinedocs/libstdc++/manual/debug.html).

Ответ 2

Скорее всего, вы видите ложные срабатывания. Я наблюдаю подобное поведение в своем коде.

В частности, предупреждения, похоже, связаны с реализацией класса общих указателей, и я понимаю, что на вашей платформе (которая, я полагаю, является x86/x86-64?) GCC использует оптимизированную инструкцию атомной сборки в ссылке общего указателя счетная техника. Проблема заключается в том, что valgrind способен обнаруживать ошибки при использовании примитивов POSIX (блокировки, мьютексы, etx.), Но он не способен справляться с примитивами нижнего уровня.

То, что я сделал до сих пор, - это просто отфильтровать из выходных данных valgrind предупреждения (возможно, вы могли бы написать некоторый файл подавления, который правильно выполняет задание).

Ответ 3

Если вы используете boost, вы можете включить использование примитивов pthreads вместо атомных операций для общих указателей. Затем вы можете использовать версию своего кода, скомпилированную с BOOST_SP_USE_PTHREADS для анализа helgrind, и вы не получите ошибок, потому что helgrind понимает примитивы pthreads.

Подробнее см. http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety.