https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113244

            Bug ID: 113244
           Summary: Potential thread sanitizer false positive with future
                    exception
           Product: gcc
           Version: 13.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: sanitizer
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gcc-bugzilla at mhxnet dot de
                CC: dodji at gcc dot gnu.org, dvyukov at gcc dot gnu.org,
                    jakub at gcc dot gnu.org, kcc at gcc dot gnu.org, marxin at 
gcc dot gnu.org
  Target Milestone: ---

Created attachment 56994
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=56994&action=edit
C++ code to reproduce the issue

Using `g++-13 (Gentoo 13.2.1_p20230826 p7) 13.2.1 20230826` and compiling the
attached program with

    g++-13 -std=c++20 -g -O2 -fsanitize=thread -fno-omit-frame-pointer -o
packaged_task packaged_task.cpp

Thread Sanitizer will likely, but not always, report several data races related
to the exception stored in the future. The races are between reading from the
exception object in the catch block on the main thread and destroying the
exception as a result of packaged_task going out of scope in one of the worker
threads.

    ==================
    WARNING: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call)
(pid=7610)
      Write of size 8 at 0x7b2c00031460 by thread T9:
        #0 ~error_base /home/mhx/src/c++/test/packaged_task.cpp:12
(packaged_task+0x5574)
        #1 ~my_error /home/mhx/src/c++/test/packaged_task.cpp:22
(packaged_task+0x5574)
        #2 std::__exception_ptr::exception_ptr::_M_release()
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:105
(libstdc++.so.6+0xb2f30)
        #3 std::__future_base::_Result<void>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:672
(packaged_task+0x5287)
        #4
std::__future_base::_Result_base::_Deleter::operator()(std::__future_base::_Result_base*)
const /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:229
(packaged_task+0x5287)
        #5 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::~unique_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/unique_ptr.h:404
(packaged_task+0x5287)
        #6 std::__future_base::_State_baseV2::~_State_baseV2()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:344
(packaged_task+0x5287)
        #7 std::__future_base::_Task_state_base<void ()>::~_Task_state_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1450
(packaged_task+0x5287)
        #8 ~_Task_state
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1477
(packaged_task+0x5287)
        #9 destroy_at<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_construct.h:88
(packaged_task+0x5287)
        #10 destroy<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:559
(packaged_task+0x5287)
        #11 _M_dispose
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:613
(packaged_task+0x5287)
        #12
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:175
(packaged_task+0x83bc)
        #13 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:361
(packaged_task+0x83bc)
        #14 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1071
(packaged_task+0x83bc)
        #15 std::__shared_ptr<std::__future_base::_Task_state_base<void ()>,
(__gnu_cxx::_Lock_policy)2>::~__shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1524
(packaged_task+0x83bc)
        #16 std::shared_ptr<std::__future_base::_Task_state_base<void ()>
>::~shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr.h:175
(packaged_task+0x83bc)
        #17 std::packaged_task<void ()>::~packaged_task()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1594
(packaged_task+0x83bc)
        #18 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:287
(packaged_task+0x645c)
        #19 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_reset() /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:318
(packaged_task+0x645c)
        #20 std::_Optional_payload<std::packaged_task<void ()>, false, false,
false>::~_Optional_payload()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:439
(packaged_task+0x645c)
        #21 std::_Optional_base<std::packaged_task<void ()>, false,
false>::~_Optional_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:510
(packaged_task+0x645c)
        #22 std::optional<std::packaged_task<void ()> >::~optional()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:705
(packaged_task+0x645c)
        #23 worker /home/mhx/src/c++/test/packaged_task.cpp:45
(packaged_task+0x645c)
        #24 void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void
(*&&)()) /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
(packaged_task+0x6760)
        #25 std::__invoke_result<void (*)()>::type std::__invoke<void
(*)()>(void (*&&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x6760)
        #26 void std::thread::_Invoker<std::tuple<void (*)()>
>::_M_invoke<0ul>(std::_Index_tuple<0ul>)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:292
(packaged_task+0x6760)
        #27 std::thread::_Invoker<std::tuple<void (*)()> >::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:299
(packaged_task+0x6760)
        #28 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
(*)()> > >::_M_run()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:244
(packaged_task+0x6760)
        #29 execute_native_thread_routine
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:104
(libstdc++.so.6+0xe157e)

      Previous read of size 8 at 0x7b2c00031460 by main thread:
        #0 main /home/mhx/src/c++/test/packaged_task.cpp:96
(packaged_task+0x3c4a)

      Location is heap block of size 168 at 0x7b2c000313e0 allocated by thread
T9:
        #0 malloc
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:681
(libtsan.so.2+0x3a963)
        #1 __cxa_allocate_exception
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_alloc.cc:398
(libstdc++.so.6+0xb1fbf)
        #2 std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>::operator()() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_function.h:591
(packaged_task+0x6bbb)
        #3
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*)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:589
(packaged_task+0x6bbb)
        #4 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*&&)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:74
(packaged_task+0x697a)
        #5 std::__invoke_result<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*>::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*&&)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x697a)
        #6 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
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:900
(packaged_task+0x697a)
        #7
std::once_flag::_Prepare_execution::_Prepare_execution<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}>(void
(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
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:836
(packaged_task+0x697a)
        #8
std::once_flag::_Prepare_execution::_Prepare_execution<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}>(void
(std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::_FUN()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:836
(packaged_task+0x697a)
        #9 pthread_once
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:1551
(libtsan.so.2+0x5d742)
        #10 __gthread_once
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/x86_64-pc-linux-gnu/bits/gthr-default.h:700
(packaged_task+0x6f4e)
        #11 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*&&)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/mutex:907
(packaged_task+0x6f4e)
        #12
std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter> ()>, bool)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:428
(packaged_task+0x62d0)
        #13 _M_run
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1494
(packaged_task+0x62d0)
        #14 std::packaged_task<void ()>::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1628
(packaged_task+0x62d0)
        #15 worker /home/mhx/src/c++/test/packaged_task.cpp:44
(packaged_task+0x62d0)
        #16 void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void
(*&&)()) /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
(packaged_task+0x6760)
        #17 std::__invoke_result<void (*)()>::type std::__invoke<void
(*)()>(void (*&&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x6760)
        #18 void std::thread::_Invoker<std::tuple<void (*)()>
>::_M_invoke<0ul>(std::_Index_tuple<0ul>)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:292
(packaged_task+0x6760)
        #19 std::thread::_Invoker<std::tuple<void (*)()> >::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:299
(packaged_task+0x6760)
        #20 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
(*)()> > >::_M_run()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:244
(packaged_task+0x6760)
        #21 execute_native_thread_routine
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:104
(libstdc++.so.6+0xe157e)

      Thread T9 (tid=7620, running) created by main thread at:
        #0 pthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036
(libtsan.so.2+0x3b7c5)
        #1 __gthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663
(libstdc++.so.6+0xe1644)
        #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State,
std::default_delete<std::thread::_State> >, void (*)())
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:172
(libstdc++.so.6+0xe1644)
        #3 std::thread& std::vector<std::thread, std::allocator<std::thread>
>::emplace_back<void (&)()>(void (&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:123
(packaged_task+0x4044)
        #4 start_workers /home/mhx/src/c++/test/packaged_task.cpp:58
(packaged_task+0x4044)
        #5 main /home/mhx/src/c++/test/packaged_task.cpp:74
(packaged_task+0x4044)

    SUMMARY: ThreadSanitizer: data race on vptr (ctor/dtor vs virtual call)
/home/mhx/src/c++/test/packaged_task.cpp:12 in ~error_base
    ==================
    ==================
    WARNING: ThreadSanitizer: data race (pid=7610)
      Write of size 8 at 0x7b2c00031468 by thread T9:
        #0 free
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:740
(libtsan.so.2+0x3ae4b)
        #1 std::__exception_ptr::exception_ptr::_M_release()
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:107
(libstdc++.so.6+0xb2f38)
        #2 std::__exception_ptr::exception_ptr::_M_release()
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:96
(libstdc++.so.6+0xb2f38)
        #3 std::__future_base::_Result<void>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:672
(packaged_task+0x5287)
        #4
std::__future_base::_Result_base::_Deleter::operator()(std::__future_base::_Result_base*)
const /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:229
(packaged_task+0x5287)
        #5 std::unique_ptr<std::__future_base::_Result_base,
std::__future_base::_Result_base::_Deleter>::~unique_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/unique_ptr.h:404
(packaged_task+0x5287)
        #6 std::__future_base::_State_baseV2::~_State_baseV2()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:344
(packaged_task+0x5287)
        #7 std::__future_base::_Task_state_base<void ()>::~_Task_state_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1450
(packaged_task+0x5287)
        #8 ~_Task_state
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1477
(packaged_task+0x5287)
        #9 destroy_at<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/stl_construct.h:88
(packaged_task+0x5287)
        #10 destroy<std::__future_base::_Task_state<main()::<lambda()>,
std::allocator<int>, void()> >
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:559
(packaged_task+0x5287)
        #11 _M_dispose
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:613
(packaged_task+0x5287)
        #12
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release_last_use()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:175
(packaged_task+0x83bc)
        #13 std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:361
(packaged_task+0x83bc)
        #14 std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1071
(packaged_task+0x83bc)
        #15 std::__shared_ptr<std::__future_base::_Task_state_base<void ()>,
(__gnu_cxx::_Lock_policy)2>::~__shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr_base.h:1524
(packaged_task+0x83bc)
        #16 std::shared_ptr<std::__future_base::_Task_state_base<void ()>
>::~shared_ptr()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/shared_ptr.h:175
(packaged_task+0x83bc)
        #17 std::packaged_task<void ()>::~packaged_task()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/future:1594
(packaged_task+0x83bc)
        #18 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_destroy()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:287
(packaged_task+0x645c)
        #19 std::_Optional_payload_base<std::packaged_task<void ()>
>::_M_reset() /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:318
(packaged_task+0x645c)
        #20 std::_Optional_payload<std::packaged_task<void ()>, false, false,
false>::~_Optional_payload()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:439
(packaged_task+0x645c)
        #21 std::_Optional_base<std::packaged_task<void ()>, false,
false>::~_Optional_base()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:510
(packaged_task+0x645c)
        #22 std::optional<std::packaged_task<void ()> >::~optional()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/optional:705
(packaged_task+0x645c)
        #23 worker /home/mhx/src/c++/test/packaged_task.cpp:45
(packaged_task+0x645c)
        #24 void std::__invoke_impl<void, void (*)()>(std::__invoke_other, void
(*&&)()) /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
(packaged_task+0x6760)
        #25 std::__invoke_result<void (*)()>::type std::__invoke<void
(*)()>(void (*&&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
(packaged_task+0x6760)
        #26 void std::thread::_Invoker<std::tuple<void (*)()>
>::_M_invoke<0ul>(std::_Index_tuple<0ul>)
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:292
(packaged_task+0x6760)
        #27 std::thread::_Invoker<std::tuple<void (*)()> >::operator()()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:299
(packaged_task+0x6760)
        #28 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void
(*)()> > >::_M_run()
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_thread.h:244
(packaged_task+0x6760)
        #29 execute_native_thread_routine
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:104
(libstdc++.so.6+0xe157e)

      Previous read of size 8 at 0x7b2c00031468 by main thread:
        #0 std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> >::_M_data() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/basic_string.h:223
(packaged_task+0x4f7d)
        #1 std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> >::c_str() const
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/basic_string.h:2584
(packaged_task+0x4f7d)
        #2 what /home/mhx/src/c++/test/packaged_task.cpp:16
(packaged_task+0x4f7d)
        #3 main /home/mhx/src/c++/test/packaged_task.cpp:96
(packaged_task+0x3c5e)

      Thread T9 (tid=7620, running) created by main thread at:
        #0 pthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libsanitizer/tsan/tsan_interceptors_posix.cpp:1036
(libtsan.so.2+0x3b7c5)
        #1 __gthread_create
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663
(libstdc++.so.6+0xe1644)
        #2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State,
std::default_delete<std::thread::_State> >, void (*)())
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/src/c++11/thread.cc:172
(libstdc++.so.6+0xe1644)
        #3 std::thread& std::vector<std::thread, std::allocator<std::thread>
>::emplace_back<void (&)()>(void (&)())
/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:123
(packaged_task+0x4044)
        #4 start_workers /home/mhx/src/c++/test/packaged_task.cpp:58
(packaged_task+0x4044)
        #5 main /home/mhx/src/c++/test/packaged_task.cpp:74
(packaged_task+0x4044)

    SUMMARY: ThreadSanitizer: data race
/var/tmp/portage/sys-devel/gcc-13.2.1_p20230826/work/gcc-13-20230826/libstdc++-v3/libsupc++/eh_ptr.cc:107
in std::__exception_ptr::exception_ptr::_M_release()
    ==================
    errors: 20000
    ThreadSanitizer: reported 2 warnings

I believe what happens is that future::get() is called on the main thread,
rethrowing the stored exception, before the packaged_task in the worker thread
goes out of scope. Hence the exception_ptr refcount is 1 by the time
packaged_task goes out of scope and the exception object is freed on the worker
thread.

I *think* the code and the behaviour are actually fine and the reported race is
a false positive.

This actually reproduces quite reliably on godbolt with both gcc-10 and gcc-13:
https://gcc.godbolt.org/z/E97exx4MP

Reply via email to