Naios / continuable

C++14 asynchronous allocation aware futures (supporting then, exception handling, coroutines and connections)
https://naios.github.io/continuable/
MIT License
815 stars 44 forks source link

Possible data race involving set_promise() & sys::transforms::wait() #38

Closed p4654545 closed 3 years ago

p4654545 commented 3 years ago

@Naios

Almost every time I run the program it will hang in the .apply(cti::transforms::wait()). Thread sanitizer also detects and reports a data race.

The compile command is in the README. continuable-bug.tar.gz


Commit Hash

Latest.

Expected Behavior

Execute the loop 20000 times.

Actual Behavior

Misses a wakeup from the set_promise() call.

Steps to Reproduce

compile and execute the program. A thread sanitizer report will appear.

You may want to comment out line 41 in main.cc

Your Environment

Naios commented 3 years ago

Could you re-test your issue with a thread safe queue from cameron314/concurrentqueue?

What is your thread sanitizer reporting exactly?

p4654545 commented 3 years ago

moodycamel::BlockingConcurrentQueue is what I use in the real application. I don't have time right now to modify the code.

Output from thread sanitizer is attached.

Just executing the single command in README and then ./a.out will also give the thread sanitizer output.

thread_sanitizer_output.txt

p4654545 commented 3 years ago

Version using moodycamel::BlockingConcurrentQueue

continuable-bug-moodycamel.tar.gz

Naios commented 3 years ago

I could reproduce it with Clang 10 and found the issue. It is indeed a race condition that happens when the waiting thread encounters a spurious wake up between https://github.com/Naios/continuable/blob/f57c5898ebfa2aa9fd52dfcd86454fe2a3cdfd57/include/continuable/detail/transforms/wait.hpp#L93-L94

The condition_variable is then notified and destroyed at the same time (or something like this).

The wait_for and wait_until with an eternal timespan are probably not affected by this bug because the frame is kept until both users have released their shared reference.


LLVMSymbolizer: error reading file: No such file or directory
==================
WARNING: ThreadSanitizer: data race (pid=3073)
  Write of size 8 at 0x7ffff08a4a78 by main thread:
    #0 pthread_cond_destroy <null> (a.out+0x426725)
    #1 cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&) <null> (a.out+0x4b6a31)
    #2 auto cti::detail::transforms::wait_and_unpack<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&) <null> (a.out+0x4b6667)
    #3 auto cti::transforms::wait()::'lambda'(auto&&)::operator()<cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> > >(auto&&) const <null> (a.out+0x4b658c)
    #4 auto cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >::apply<cti::transforms::wait()::'lambda'(auto&&)>(auto&&) && <null> (a.out+0x4b4e1a)
    #5 main <null> (a.out+0x4b486a)

  Previous read of size 8 at 0x7ffff08a4a78 by thread T1:
    #0 pthread_cond_broadcast <null> (a.out+0x426592)
    #1 std::condition_variable::notify_all() <null> (libstdc++.so.6+0xcb228)
    #2 auto cti::detail::util::detail::invocation_env<0ul>::partial_invoke_impl_shortcut<cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...)>(std::integral_constant<bool, true>, cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&, cti::detail::identity<>&&...) <null> (a.out+0x4b8380)
    #3 auto cti::detail::util::partial_invoke<0ul, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...)>(std::integral_constant<unsigned long, cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0> >, cti::detail::identity<>&&, cti::result<>&&...) <null> (a.out+0x4b8320)
    #4 auto cti::detail::base::decoration::invoke_callback<cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...)>(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&, cti::detail::identity<>&&...) <null> (a.out+0x4b82b0)
    #5 auto cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...)::operator()<cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...), cti::detail::base::callbacks::final_callback<> >(auto&&, auto&&, auto&&...) const <null> (a.out+0x4b818b)
    #6 void cti::detail::base::on_executor<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...), cti::detail::base::callbacks::final_callback<> >(cti::detail::types::this_thread_executor_tag, auto&&, auto&&, cti::result<>&&, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...)&&...) <null> (a.out+0x4b811a)
    #7 cti::detail::base::callbacks::proto::result_handler_base<(cti::detail::base::handle_results)1, cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> >, cti::detail::identity<> >::operator()() && <null> (a.out+0x4b8086)
    #8 cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, main::$_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> >::set_value() <null> (a.out+0x4bb530)
    #9 auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&)::'lambda'()::operator()() <null> (a.out+0x4bb4e8)
    #10 auto cti::detail::util::detail::invocation_env<0ul>::partial_invoke_impl_shortcut<auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&)::'lambda'()>(std::integral_constant<bool, true>, cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&, cti::detail::identity<>&&...) <null> (a.out+0x4bb4b0)
    #11 auto cti::detail::util::partial_invoke<0ul, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&)::'lambda'()>(std::integral_constant<unsigned long, cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0> >, cti::detail::identity<>&&, cti::result<>&&...) <null> (a.out+0x4bb450)
    #12 auto cti::detail::base::decoration::invoke_callback<auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<> >&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&)::'lambda'()>(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&, cti::detail::identity<>&&...) <null> (a.out+0x4bb3e0)
    #13 auto cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...)::operator()<auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >(auto&&, auto&&, auto&&...) const <null> (a.out+0x4bb2bb)
    #14 auto cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >::set_value()::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...)::operator()<>(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...) const <null> (a.out+0x4bb24a)
    #15 decltype(std::forward<auto>(fp)(get<cti::result<> >(std::forward<auto>(fp0))...)) cti::detail::traits::detail_unpack::unpack_impl<cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >::set_value()::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), std::tuple<> >(auto&&, auto&&, std::integer_sequence<unsigned long, cti::result<>...>) <null> (a.out+0x4bb18c)
    #16 decltype(detail_unpack::unpack_impl(std::forward<auto>(fp), std::forward<auto>(fp0), cti::result<>{})) cti::detail::traits::unpack<cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >::set_value()::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), std::tuple<>, std::integer_sequence<unsigned long> >(auto&&, auto&&) <null> (a.out+0x4bb11d)
    #17 cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >::set_value() <null> (a.out+0x4bb0af)
    #18 cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >::operator()() && <null> (a.out+0x4bb050)
    #19 decltype(std::forward<auto>(fp)(std::forward<cti::detail::identity<> >(fp0)...)) fu2::abi_400::detail::invocation::invoke<cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> > >(auto&&, cti::detail::identity<>&&...) <null> (a.out+0x4bb010)
    #20 fu2::abi_400::detail::type_erasure::invocation_table::function_trait<void () &&>::internal_invoker<fu2::abi_400::detail::type_erasure::box<false, cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> >, std::allocator<cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<cti::detail::base::decoration::invoker_of(cti::detail::identity<void>)::'lambda'(auto&&, auto&&, auto&&...), cti::detail::identity<> >, auto main::$_0::operator()<cti::detail::base::callbacks::callback_base<cti::detail::identity<>, (cti::detail::base::handle_results)1, (cti::detail::base::handle_errors)1, cti::result<> cti::detail::transforms::wait_relaxed<cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>, cti::detail::identity<>, cti::result<> >(cti::continuable_base<auto, auto>&&)::'lambda'(cti::detail::base::proxy_continuable<cti::detail::identity<>, $_0>&&...), cti::detail::types::this_thread_executor_tag, cti::detail::base::callbacks::final_callback<> > >(auto&&)::'lambda'(), cti::detail::base::callbacks::final_callback<> > > >, false>::invoke(fu2::abi_400::detail::type_erasure::data_accessor*, unsigned long) <null> (a.out+0x4bbbcd)
    #21 decltype(auto) fu2::abi_400::detail::type_erasure::tables::vtable<fu2::abi_400::detail::property<true, false, void () &&, void (cti::exception_arg_t, std::__exception_ptr::exception_ptr) &&> >::invoke<0ul, fu2::abi_400::detail::type_erasure::data_accessor*, unsigned long const&>(fu2::abi_400::detail::type_erasure::data_accessor*&&, unsigned long const&) const <null> (a.out+0x4cdf8d)
    #22 decltype(auto) fu2::abi_400::detail::type_erasure::erasure<true, fu2::abi_400::detail::config<true, false, fu2::capacity_fixed<32ul, 16ul> >, fu2::abi_400::detail::property<true, false, void () &&, void (cti::exception_arg_t, std::__exception_ptr::exception_ptr) &&> >::invoke<0ul, fu2::abi_400::detail::type_erasure::erasure<true, fu2::abi_400::detail::config<true, false, fu2::capacity_fixed<32ul, 16ul> >, fu2::abi_400::detail::property<true, false, void () &&, void (cti::exception_arg_t, std::__exception_ptr::exception_ptr) &&> > >(fu2::abi_400::detail::type_erasure::erasure<true, fu2::abi_400::detail::config<true, false, fu2::capacity_fixed<32ul, 16ul> >, fu2::abi_400::detail::property<true, false, void () &&, void (cti::exception_arg_t, std::__exception_ptr::exception_ptr) &&> >&&) <null> (a.out+0x4cdeac)
    #23 fu2::abi_400::detail::type_erasure::invocation_table::operator_impl<0ul, fu2::abi_400::detail::function<fu2::abi_400::detail::config<true, false, fu2::capacity_fixed<32ul, 16ul> >, fu2::abi_400::detail::property<true, false, void () &&, void (cti::exception_arg_t, std::__exception_ptr::exception_ptr) &&> >, void () &&, void (cti::exception_arg_t, std::__exception_ptr::exception_ptr) &&>::operator()() && <null> (a.out+0x4cddf0)
    #24 cti::detail::erasure::work::operator()() && <null> (a.out+0x4cdd10)
    #25 cti::promise_base<cti::detail::erasure::work, cti::detail::identity<> >::set_value() <null> (a.out+0x4cd4e7)
    #26 main::$_3::operator()() const <null> (a.out+0x4c4031)
    #27 void std::__invoke_impl<void, main::$_3>(std::__invoke_other, main::$_3&&) <null> (a.out+0x4c3f70)
    #28 std::__invoke_result<main::$_3>::type std::__invoke<main::$_3>(main::$_3&&) <null> (a.out+0x4c3eb0)
    #29 decltype(std::__invoke(_S_declval<0ul>())) std::thread::_Invoker<std::tuple<main::$_3> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) <null> (a.out+0x4c3e58)
    #30 std::thread::_Invoker<std::tuple<main::$_3> >::operator()() <null> (a.out+0x4c3df8)
    #31 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::$_3> > >::_M_run() <null> (a.out+0x4c3c2f)
    #32 <null> <null> (libstdc++.so.6+0xd0b0f)

  Location is stack of main thread.

  Location is global '??' at 0x7ffff0886000 ([stack]+0x00000001ea78)

  Thread T1 (tid=3075, running) created by main thread at:
    #0 pthread_create <null> (a.out+0x4252fb)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xd0da4)
    #2 main <null> (a.out+0x4b4806)

SUMMARY: ThreadSanitizer: data race (/data/denis/continuable-bug/a.out+0x426725) in pthread_cond_destroy
==================
ThreadSanitizer: reported 1 warnings
p4654545 commented 3 years ago

Important note:

Because of using a std::variant<cti::work,...> I had to change line 1425 of function2.hpp from:

std::is_copy_constructible<std::decay_t<T>>::value

to

std::is_copy_constructible_v<std::decay_t<T>>

Naios commented 3 years ago

Could you test whether https://github.com/Naios/continuable/commit/6bffb44d2bcca4989945e4d76ce8d35cf8cdcebd fixes your issue?

p4654545 commented 3 years ago

Thread sanitizer is not complaining anymore and both the repro scenario and the application behave correctly now. Thank you!