NVIDIA / stdexec

`std::execution`, the proposed C++ framework for asynchronous and parallel programming.
Apache License 2.0
1.56k stars 159 forks source link

Thread Sanitizer Report in test_split.cpp #819

Closed maikel closed 6 months ago

maikel commented 1 year ago

While looking for data races TSAN threw in one run of test.stdexec the following verbose warning at test_split.cpp:319 (test: "split is thread-safe")

Maybe this is related to #772

==================
WARNING: ThreadSanitizer: data race (pid=33567)
  Read of size 1 at 0x7b2800000170 by thread T12:
    #0 std::__detail::__variant::_Variant_storage<false, std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> >::_M_valid() const /usr/include/c++/11/variant:450 (test.stdexec+0x301dc0)
    #1 std::variant<std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> >::valueless_by_exception() const /usr/include/c++/11/variant:1618 (test.stdexec+0x2ff4ae)
    #2 visit<stdexec::__split::__operation<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env, stdexec::_Y<stdexec::__schedule_from::__receiver1<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, stdexec::__then::__receiver<stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> > >::__t> >::__t::__notify(stdexec::__split::__operation_base*)::<lambda(const auto:51&)>, std::variant<std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> >&> /usr/include/c++/11/variant:1772 (test.stdexec+0x2c2d47)
    #3 __notify /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:3416 (test.stdexec+0x2be589)
    #4 tag_invoke /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:3442 (test.stdexec+0x2b8b27)
    #5 operator()<stdexec::__start::start_t, stdexec::__split::__operation<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env, stdexec::_Y<stdexec::__schedule_from::__receiver1<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, stdexec::__then::__receiver<stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> > >::__t> >::__t&> /home/mnadolski/Development/stdexec/include/stdexec/functional.hpp:106 (test.stdexec+0x2b1943)
    #6 operator()<stdexec::__split::__operation<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env, stdexec::_Y<stdexec::__schedule_from::__receiver1<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, stdexec::__then::__receiver<stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> > >::__t> >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:1224 (test.stdexec+0x2abf93)
    #7 tag_invoke /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:4658 (test.stdexec+0x2a62a4)
    #8 operator()<stdexec::__start::start_t, stdexec::__schedule_from::__operation1<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, stdexec::__then::__receiver<stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> > >::__t&> /home/mnadolski/Development/stdexec/include/stdexec/functional.hpp:106 (test.stdexec+0x2a2943)
    #9 operator()<stdexec::__schedule_from::__operation1<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, stdexec::__then::__receiver<stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> > >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:1224 (test.stdexec+0x29ea09)
    #10 tag_invoke /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2731 (test.stdexec+0x29a79f)
    #11 operator()<stdexec::__start::start_t, stdexec::__then::__operation<stdexec::__schedule_from::__sender<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env> >::__t, stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> >::__t&> /home/mnadolski/Development/stdexec/include/stdexec/functional.hpp:106 (test.stdexec+0x294f71)
    #12 operator()<stdexec::__then::__operation<stdexec::__schedule_from::__sender<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env> >::__t, stdexec::__sync_wait::__receiver<int>, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:1224 (test.stdexec+0x28fdab)
    #13 operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<inline_scheduler, stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env> >, ____C_A_T_C_H____T_E_S_T____28()::<lambda()>::<lambda(int)> >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:5894 (test.stdexec+0x2892c6)
    #14 operator() /home/mnadolski/Development/stdexec/test/stdexec/algos/adaptors/test_split.cpp:319 (test.stdexec+0x2823fa)
    #15 __invoke_impl<void, ____C_A_T_C_H____T_E_S_T____28()::<lambda()> > /usr/include/c++/11/bits/invoke.h:61 (test.stdexec+0x2f5040)
    #16 __invoke<____C_A_T_C_H____T_E_S_T____28()::<lambda()> > /usr/include/c++/11/bits/invoke.h:96 (test.stdexec+0x2f4fb7)
    #17 _M_invoke<0> /usr/include/c++/11/bits/std_thread.h:253 (test.stdexec+0x2f4f18)
    #18 operator() /usr/include/c++/11/bits/std_thread.h:260 (test.stdexec+0x2f499c)
    #19 _M_run /usr/include/c++/11/bits/std_thread.h:211 (test.stdexec+0x2f44da)
    #20 <null> <null> (libstdc++.so.6+0xdc2b2)

  Previous write of size 1 at 0x7b2800000170 by thread T9:
    #0 std::__detail::__variant::_Variant_storage<false, std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> >::_M_reset() /usr/include/c++/11/variant:432 (test.stdexec+0x310136)
    #1 std::enable_if<is_constructible_v<std::variant_alternative<2ul, std::variant<std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> > >::type, stdexec::__receivers::set_value_t&, int>, std::variant_alternative<2ul, std::variant<std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> > >::type&>::type std::variant<std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> >::emplace<2ul, stdexec::__receivers::set_value_t&, int>(stdexec::__receivers::set_value_t&, int&&) /usr/include/c++/11/variant:1528 (test.stdexec+0x30d8d8)
    #2 std::enable_if<(is_constructible_v<std::tuple<stdexec::__receivers::set_value_t, int>, stdexec::__receivers::set_value_t&, int>)&&(__exactly_once<std::tuple<stdexec::__receivers::set_value_t, int> >), std::tuple<stdexec::__receivers::set_value_t, int>&>::type std::variant<std::tuple<stdexec::__receivers::set_stopped_t>, std::tuple<stdexec::__receivers::set_error_t, std::__exception_ptr::exception_ptr>, std::tuple<stdexec::__receivers::set_value_t, int> >::emplace<std::tuple<stdexec::__receivers::set_value_t, int>, stdexec::__receivers::set_value_t&, int>(stdexec::__receivers::set_value_t&, int&&) /usr/include/c++/11/variant:1502 (test.stdexec+0x30abea)
    #3 tag_invoke<stdexec::__receivers::set_value_t, int> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:3294 (test.stdexec+0x2f1b04)
    #4 operator()<stdexec::__receivers::set_value_t, stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, int> /home/mnadolski/Development/stdexec/include/stdexec/functional.hpp:106 (test.stdexec+0x2f18f6)
    #5 operator()<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, int> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:365 (test.stdexec+0x2f17c1)
    #6 __set_value_invoke_<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)>, std::chrono::duration<long int, std::ratio<1, 1000000> > > /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2634 (test.stdexec+0x2f1633)
    #7 __set_value_invoke<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)>, std::chrono::duration<long int, std::ratio<1, 1000000> > > /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2644 (test.stdexec+0x2f14b3)
    #8 tag_invoke<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > > /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2694 (test.stdexec+0x2f13b0)
    #9 operator()<stdexec::__receivers::set_value_t, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, std::chrono::duration<long int, std::ratio<1, 1000000> > > /home/mnadolski/Development/stdexec/include/stdexec/functional.hpp:106 (test.stdexec+0x2f1254)
    #10 operator()<stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, std::chrono::duration<long int, std::ratio<1, 1000000> > > /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:365 (test.stdexec+0x2f1171)
    #11 operator()<std::chrono::duration<long int, std::ratio<1, 1000000> >, stdexec::__receivers::set_value_t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:4670 (test.stdexec+0x2f1071)
    #12 __invoke_impl<void, stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>::<lambda(auto:57, _Args& ...)>, stdexec::__receivers::set_value_t&, std::chrono::duration<long int, std::ratio<1, 1000000> >&> /usr/include/c++/11/bits/invoke.h:61 (test.stdexec+0x2f0f39)
    #13 __invoke<stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>::<lambda(auto:57, _Args& ...)>, stdexec::__receivers::set_value_t&, std::chrono::duration<long int, std::ratio<1, 1000000> >&> /usr/include/c++/11/bits/invoke.h:96 (test.stdexec+0x2f0cd8)
    #14 __apply_impl<stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>::<lambda(auto:57, _Args& ...)>, std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > >&, 0, 1> /usr/include/c++/11/tuple:1858 (test.stdexec+0x2f0795)
    #15 apply<stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>::<lambda(auto:57, _Args& ...)>, std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > >&> /usr/include/c++/11/tuple:1869 (test.stdexec+0x2f0800)
    #16 operator()<std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > > > /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:4668 (test.stdexec+0x2f0887)
    #17 __invoke_impl<void, stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>, std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > >&> /usr/include/c++/11/bits/invoke.h:61 (test.stdexec+0x2eff7d)
    #18 __invoke<stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>, std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > >&> /usr/include/c++/11/bits/invoke.h:96 (test.stdexec+0x2ef861)
    #19 __visit_invoke /usr/include/c++/11/variant:1035 (test.stdexec+0x2eee9e)
    #20 __do_visit<std::__detail::__variant::__deduce_visit_result<void>, stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>, std::variant<std::monostate, std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > > >&> /usr/include/c++/11/variant:1761 (test.stdexec+0x2eef8e)
    #21 visit<stdexec::__schedule_from::__operation1<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t::__complete()::<lambda(_Tup&)>, std::variant<std::monostate, std::tuple<stdexec::__receivers::set_value_t, std::chrono::duration<long int, std::ratio<1, 1000000> > > >&> /usr/include/c++/11/variant:1795 (test.stdexec+0x2ef01c)
    #22 __complete /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:4663 (test.stdexec+0x2edf79)
    #23 tag_invoke /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:4567 (test.stdexec+0x2ecfc7)
    #24 operator()<stdexec::__receivers::set_value_t, stdexec::__schedule_from::__receiver2<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t> /home/mnadolski/Development/stdexec/include/stdexec/functional.hpp:106 (test.stdexec+0x2ec24b)
    #25 operator()<stdexec::__schedule_from::__receiver2<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > >, stdexec::__then::__receiver<stdexec::__split::__receiver<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> > >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:365 (test.stdexec+0x2eb1ed)
    #26 operator() /home/mnadolski/Development/stdexec/include/exec/static_thread_pool.hpp:496 (test.stdexec+0x2ea338)
    #27 _FUN /home/mnadolski/Development/stdexec/include/exec/static_thread_pool.hpp:488 (test.stdexec+0x2ea387)
    #28 exec::static_thread_pool::run(unsigned int) /home/mnadolski/Development/stdexec/include/exec/static_thread_pool.hpp:567 (test.stdexec+0x1627f3)
    #29 exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}::operator()() const /home/mnadolski/Development/stdexec/include/exec/static_thread_pool.hpp:524 (test.stdexec+0x16229e)
    #30 void std::__invoke_impl<void, exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}>(std::__invoke_other, exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}&&) /usr/include/c++/11/bits/invoke.h:61 (test.stdexec+0x16670f)
    #31 std::__invoke_result<exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}>::type std::__invoke<exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}>(exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}&&) /usr/include/c++/11/bits/invoke.h:96 (test.stdexec+0x16669c)
    #32 void std::thread::_Invoker<std::tuple<exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /usr/include/c++/11/bits/std_thread.h:253 (test.stdexec+0x1665f2)
    #33 std::thread::_Invoker<std::tuple<exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}> >::operator()() /usr/include/c++/11/bits/std_thread.h:260 (test.stdexec+0x166594)
    #34 std::thread::_State_impl<std::thread::_Invoker<std::tuple<exec::static_thread_pool::static_thread_pool(unsigned int)::{lambda()#1}> > >::_M_run() /usr/include/c++/11/bits/std_thread.h:211 (test.stdexec+0x166546)
    #35 <null> <null> (libstdc++.so.6+0xdc2b2)

  Location is heap block of size 160 at 0x7b2800000140 allocated by main thread:
    #0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.0+0x8f162)
    #1 allocate /usr/include/c++/11/ext/new_allocator.h:127 (test.stdexec+0x2ca9a2)
    #2 allocate /usr/include/c++/11/bits/allocator.h:185 (test.stdexec+0x2c2598)
    #3 allocate /usr/include/c++/11/bits/alloc_traits.h:464 (test.stdexec+0x2c2598)
    #4 __allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, std::allocator<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t>, (__gnu_cxx::_Lock_policy)2> > > /usr/include/c++/11/bits/allocated_ptr.h:98 (test.stdexec+0x2bdfc6)
    #5 __shared_count<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, std::allocator<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t>, stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__env::empty_env> /usr/include/c++/11/bits/shared_ptr_base.h:648 (test.stdexec+0x2b8703)
    #6 __shared_ptr<std::allocator<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t>, stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__env::empty_env> /usr/include/c++/11/bits/shared_ptr_base.h:1342 (test.stdexec+0x2b1785)
    #7 shared_ptr<std::allocator<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t>, stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__env::empty_env> /usr/include/c++/11/bits/shared_ptr.h:409 (test.stdexec+0x2abed4)
    #8 allocate_shared<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, std::allocator<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t>, stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__env::empty_env> /usr/include/c++/11/bits/shared_ptr.h:863 (test.stdexec+0x2a61be)
    #9 make_shared<stdexec::__split::__sh_state<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__env::empty_env> /usr/include/c++/11/bits/shared_ptr.h:879 (test.stdexec+0x2a2827)
    #10 __t /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:3481 (test.stdexec+0x29e876)
    #11 operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__env::empty_env> /home/mnadolski/Development/stdexec/include/stdexec/__detail/__meta.hpp:722 (test.stdexec+0x29a50f)
    #12 operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:3561 (test.stdexec+0x294b03)
    #13 operator() /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2395 (test.stdexec+0x28f580)
    #14 __invoke_impl<stdexec::__split::__sender<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >, stdexec::__env::empty_env>::__t, stdexec::__closure::__binder_back<stdexec::__split::split_t>::operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t>(stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t&&) &&::<lambda()> > /usr/include/c++/11/bits/invoke.h:61 (test.stdexec+0x29a582)
    #15 __invoke<stdexec::__closure::__binder_back<stdexec::__split::split_t>::operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t>(stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t&&) &&::<lambda()> > /usr/include/c++/11/bits/invoke.h:97 (test.stdexec+0x294b98)
    #16 __apply_impl<stdexec::__closure::__binder_back<stdexec::__split::split_t>::operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t>(stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t&&) &&::<lambda()>, std::tuple<>&> /usr/include/c++/11/tuple:1859 (test.stdexec+0x28f60b)
    #17 apply<stdexec::__closure::__binder_back<stdexec::__split::split_t>::operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t>(stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t&&) &&::<lambda()>, std::tuple<>&> /usr/include/c++/11/tuple:1871 (test.stdexec+0x28f6a8)
    #18 operator()<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t> /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2397 (test.stdexec+0x28f75b)
    #19 operator|<stdexec::__then::__sender<stdexec::__schedule_from::__sender<exec::static_thread_pool::scheduler, stdexec::__just::__sender<std::chrono::duration<long int, std::ratio<1, 1000000> > > >, ____C_A_T_C_H____T_E_S_T____28()::<lambda(std::chrono::microseconds)> >::__t, stdexec::__closure::__binder_back<stdexec::__split::split_t> > /home/mnadolski/Development/stdexec/include/stdexec/execution.hpp:2376 (test.stdexec+0x288e53)
    #20 ____C_A_T_C_H____T_E_S_T____28 /home/mnadolski/Development/stdexec/test/stdexec/algos/adaptors/test_split.cpp:301 (test.stdexec+0x282693)
    #21 Catch::TestInvokerAsFunction::invoke() const /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:14317 (test.stdexec+0x431e0)
    #22 Catch::TestCase::invoke() const /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:14156 (test.stdexec+0x41f92)
    #23 Catch::RunContext::invokeActiveTestCase() /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:13016 (test.stdexec+0x3aabb)
    #24 Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:12989 (test.stdexec+0x3a6fc)
    #25 Catch::RunContext::runTest(Catch::TestCase const&) /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:12750 (test.stdexec+0x387dd)
    #26 execute /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:13343 (test.stdexec+0x3cb83)
    #27 Catch::Session::runInternal() /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:13549 (test.stdexec+0x3e42a)
    #28 Catch::Session::run() /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:13505 (test.stdexec+0x3e00e)
    #29 int Catch::Session::run<char>(int, char const* const*) <null> (test.stdexec+0x9c167)
    #30 main /home/mnadolski/Development/stdexec-build-gcc/_deps/catch2-src/single_include/catch2/catch.hpp:17504 (test.stdexec+0x5aeb8)

[ ... ]

SUMMARY: ThreadSanitizer: data race /usr/include/c++/11/tuple:193 in std::_Head_base<1ul, int, false>::_Head_base(int const&)
==================
maikel commented 1 year ago

I've looked at this for about an hour and I am not sure if that is actually a code-gen problem of GCC-11. Still investigating.

ccotter commented 6 months ago

I think this also reproduces the same TSAN diagnostic:

#include <exec/single_thread_context.hpp>
#include <stdexec/execution.hpp>

int main() {
    exec::single_thread_context ctx1;
    exec::single_thread_context ctx2;

    namespace ex = stdexec;
    for (int i = 0; i != 10000; ++i) { // enough times to trigger the diagnostic
        std::atomic<int> counter{0};
        auto split_sndr = ex::just(50) | ex::split() | ex::then([](int x) { return x*2; });

        auto sndr = ex::just() | ex::let_value([&counter, split_sndr]() {
            ++counter;
            while (counter != 2) {
                // wait
            }
            return split_sndr;
        });

        ex::sync_wait(
            ex::when_all(
                ex::on(ctx1.get_scheduler(), sndr),
                ex::on(ctx2.get_scheduler(), sndr))
            | ex::then([](auto&&...) { })
        );
    }
    return 0;
}

I can't explain why, but changing https://github.com/NVIDIA/stdexec/blob/08619a4/include/stdexec/execution.hpp#L3369 to memory_order_acq_rel silences the diagnostic for my example and the "split is thread-safe" test. From what I can tell, all of the code paths I can think of have a correct release-acquire ordering.

maikel commented 6 months ago

Seeing the potential culprit makes we wonder if we can reduce this into a small piece and see who needs a fix, (us, tsan, gcc)

ccotter commented 6 months ago

Seeing the potential culprit makes we wonder if we can reduce this into a small piece and see who needs a fix, (us, tsan, gcc)

Good idea. I'll see if I can do that.

ccotter commented 6 months ago

This mostly minimal reproducer mimics the same set of atomic operations as what split is doing in the test cases. It looks like GCC-11 flags the code, but GCC-12 does not. Similarly, clang-12 flags the code, but clang-13 does not. I couldn't find anything in my limited search through the commits between clang-12 and 13, but I'm assuming this was a false positive fixed in TSAN itself.

I also confirmed the test_split TSAN error (which I can reliably reproduce if I run the "split is thread-safe" test in an infinite loop) goes away with GCC-12, but is present with GCC-11.