From 22eac6c4522d7d0591aa089cca9c3bcd8aae90b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 21 Mar 2026 14:58:54 +0000 Subject: [PATCH 1/9] added a sender to avoid environments getting stale too early --- examples/CMakeLists.txt | 2 +- include/beman/execution/detail/affine_on.hpp | 19 ++- .../execution/detail/sender_awaitable.hpp | 18 +-- .../beman/execution/detail/store_receiver.hpp | 123 ++++++++++++++++++ src/beman/execution/CMakeLists.txt | 2 + src/beman/execution/store_receiver.cppm | 11 ++ 6 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 include/beman/execution/detail/store_receiver.hpp create mode 100644 src/beman/execution/store_receiver.cppm diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f271e6cf..0f34e145 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -17,6 +17,7 @@ if(PROJECT_IS_TOP_LEVEL) enable_testing() endif() +set(TODO stop_token) #-dk:TODO set(EXAMPLES allocator doc-just @@ -29,7 +30,6 @@ set(EXAMPLES playground sender-demo stackoverflow - stop_token stopping when_all-cancel ) diff --git a/include/beman/execution/detail/affine_on.hpp b/include/beman/execution/detail/affine_on.hpp index cc5e4aaa..45f2bde1 100644 --- a/include/beman/execution/detail/affine_on.hpp +++ b/include/beman/execution/detail/affine_on.hpp @@ -37,6 +37,7 @@ import beman.execution.detail.sender_adaptor_closure; import beman.execution.detail.sender_for; import beman.execution.detail.sender_has_affine_on; import beman.execution.detail.set_value; +import beman.execution.detail.store_receiver; import beman.execution.detail.tag_of_t; import beman.execution.detail.transform_sender; import beman.execution.detail.write_env; @@ -57,6 +58,8 @@ import beman.execution.detail.write_env; #include #include #include +#include +#include #include #include #include @@ -152,11 +155,17 @@ struct affine_on_t : ::beman::execution::sender_adaptor_closure { constexpr child_tag_t t{}; return t.affine_on(::beman::execution::detail::forward_like(child), ev); } else { - return ::beman::execution::write_env( - ::beman::execution::schedule_from( - ::beman::execution::get_scheduler(ev), - ::beman::execution::write_env(::beman::execution::detail::forward_like(child), ev)), - beman::execution::detail::affine_on_env(ev)); + return + ::beman::execution::detail::store_receiver( + ::beman::execution::detail::forward_like(child), + [](Child&& child, const auto& ev) { + return ::beman::execution::write_env( + ::beman::execution::schedule_from( + ::beman::execution::get_scheduler(ev), + ::beman::execution::write_env(::std::forward(child), ev)), + ::beman::execution::detail::affine_on_env(ev)) + ; + }); } } template diff --git a/include/beman/execution/detail/sender_awaitable.hpp b/include/beman/execution/detail/sender_awaitable.hpp index 59edde18..16f43275 100644 --- a/include/beman/execution/detail/sender_awaitable.hpp +++ b/include/beman/execution/detail/sender_awaitable.hpp @@ -12,7 +12,6 @@ import std; #include #include #include -#include #include #include #include @@ -49,20 +48,19 @@ import beman.execution.detail.unspecified_promise; namespace beman::execution::detail { template class sender_awaitable { + static inline constexpr bool enable_defence{true}; struct unit {}; using value_type = ::beman::execution::detail::single_sender_value_type>; using result_type = ::std::conditional_t<::std::is_void_v, unit, value_type>; using variant_type = ::std::variant<::std::monostate, result_type, ::std::exception_ptr>; - using data_type = ::std::tuple, ::std::coroutine_handle>; + using data_type = ::std::tuple, ::std::coroutine_handle>; struct awaitable_receiver { using receiver_concept = ::beman::execution::receiver_t; void resume() { - std::thread::id id(::std::this_thread::get_id()); - if (not ::std::get<1>(*result_ptr_) - .compare_exchange_strong(id, ::std::thread::id{}, std::memory_order_acq_rel)) { + if (not enable_defence || ::std::get<1>(*result_ptr_).exchange(true, std::memory_order_acq_rel)) { ::std::get<2>(*result_ptr_).resume(); } } @@ -85,9 +83,7 @@ class sender_awaitable { } void set_stopped() && noexcept { - std::thread::id id(::std::this_thread::get_id()); - if (not ::std::get<1>(*result_ptr_) - .compare_exchange_strong(id, ::std::thread::id{}, ::std::memory_order_acq_rel)) { + if (not enable_defence || ::std::get<1>(*result_ptr_).exchange(true, ::std::memory_order_acq_rel)) { static_cast<::std::coroutine_handle<>>(::std::get<2>(*result_ptr_).promise().unhandled_stopped()) .resume(); } @@ -107,16 +103,14 @@ class sender_awaitable { public: sender_awaitable(Sndr&& sndr, Promise& p) - : result{::std::monostate{}, ::std::this_thread::get_id(), ::std::coroutine_handle::from_promise(p)}, + : result{::std::monostate{}, false, ::std::coroutine_handle::from_promise(p)}, state{::beman::execution::connect(::std::forward(sndr), sender_awaitable::awaitable_receiver{::std::addressof(result)})} {} static constexpr bool await_ready() noexcept { return false; } ::std::coroutine_handle<> await_suspend(::std::coroutine_handle handle) noexcept { ::beman::execution::start(state); - ::std::thread::id id(::std::this_thread::get_id()); - if (not ::std::get<1>(this->result) - .compare_exchange_strong(id, ::std::thread::id{}, ::std::memory_order_acq_rel)) { + if (enable_defence && ::std::get<1>(this->result).exchange(true, std::memory_order_acq_rel)) { if (::std::holds_alternative<::std::monostate>(::std::get<0>(this->result))) { return ::std::get<2>(this->result).promise().unhandled_stopped(); } diff --git a/include/beman/execution/detail/store_receiver.hpp b/include/beman/execution/detail/store_receiver.hpp new file mode 100644 index 00000000..c23d8462 --- /dev/null +++ b/include/beman/execution/detail/store_receiver.hpp @@ -0,0 +1,123 @@ +// include/beman/execution/detail/store_receiver.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_STORE_RECEIVER +#define INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_STORE_RECEIVER + +#include +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#include +#include +#endif +#ifdef BEMAN_HAS_MODULES +import beman.execution.detail.connect; +import beman.execution.detail.connect_result_t; +import beman.execution.detail.env_of_t; +import beman.execution.detail.get_completion_signatures; +import beman.execution.detail.get_env; +import beman.execution.detail.operation_state; +import beman.execution.detail.receiver; +import beman.execution.detail.sender; +import beman.execution.detail.set_error; +import beman.execution.detail.set_stopped; +import beman.execution.detail.set_value; +import beman.execution.detail.start; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +// ---------------------------------------------------------------------------- + +namespace beman::execution::detail { + struct store_receiver_t { + template <::beman::execution::receiver Rcvr> + struct receiver { + using receiver_concept = ::beman::execution::receiver_t; + Rcvr* rcvr; + template + auto set_value(Args&&... args) && noexcept -> void { + ::beman::execution::set_value(::std::move(*this->rcvr), ::std::forward(args)...); + } + template + auto set_error(Error&& error) && noexcept -> void { + ::beman::execution::set_error(::std::move(*this->rcvr), ::std::forward(error)); + } + auto set_stopped() && noexcept -> void { + ::beman::execution::set_stopped(::std::move(*this->rcvr)); + } + auto get_env() const noexcept { + return ::beman::execution::get_env(*this->rcvr); + } + }; + template <::beman::execution::sender Sndr, typename Trans, ::beman::execution::receiver Rcvr> + struct state { + using operation_state_concept = ::beman::execution::operation_state_t; + using env_t = ::beman::execution::env_of_t; + using state_t = ::beman::execution::connect_result_t< + decltype(::std::declval()(::std::declval(), ::std::declval())), + receiver>; + Rcvr rcvr; + state_t op_state; + template <::beman::execution::sender S, typename T, ::beman::execution::receiver R> + state(S&& sndr, T&& trans, R&& r) : + rcvr(::std::forward(::std::forward(r))) + ,op_state( + ::beman::execution::connect( + ::std::forward(trans)(::std::forward(sndr), ::beman::execution::get_env(this->rcvr)), + receiver{::std::addressof(this->rcvr)} + ) + ) + {} + auto start() & noexcept { + ::beman::execution::start(this->op_state); + } + }; + template <::beman::execution::sender Sndr, typename Trans> + struct sender { + using sender_concept = ::beman::execution::sender_t; + template + static consteval auto get_completion_signatures(Env&&... env) noexcept { + return ::beman::execution::get_completion_signatures<::std::remove_cvref_t, Env...>(); + } + ::std::remove_cvref_t sndr; + ::std::remove_cvref_t trans; + + template <::beman::execution::receiver Receiver> + auto connect(Receiver&& r) && { + static_assert(::beman::execution::operation_state>>); + return state>(::std::move(this->sndr), ::std::move(this->trans), ::std::forward(r)); + } + template <::beman::execution::receiver Receiver> + auto connect(Receiver&& r) const& { + static_assert(::beman::execution::operation_state>>); + return state>(this->sndr, this->trans, ::std::forward(r)); + } + }; + template <::beman::execution::sender Sndr, typename Trans> + auto operator()(Sndr&& sndr, Trans&& trans) const { + static_assert(::beman::execution::sender>); + return sender{::std::forward(sndr), ::std::forward(trans)}; + } + }; + + inline constexpr store_receiver_t store_receiver{}; +} + + +// ---------------------------------------------------------------------------- + +#endif diff --git a/src/beman/execution/CMakeLists.txt b/src/beman/execution/CMakeLists.txt index ccbaa2b4..8585a84a 100644 --- a/src/beman/execution/CMakeLists.txt +++ b/src/beman/execution/CMakeLists.txt @@ -178,6 +178,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/stoppable_token.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/stopped_as_error.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/stopped_as_optional.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/store_receiver.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/suppress_pop.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/suppress_push.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/suspend_complete.hpp @@ -368,6 +369,7 @@ if(BEMAN_USE_MODULES) stoppable_token.cppm stopped_as_error.cppm stopped_as_optional.cppm + store_receiver.cppm suspend_complete.cppm sync_wait.cppm sync_wait_with_variant.cppm diff --git a/src/beman/execution/store_receiver.cppm b/src/beman/execution/store_receiver.cppm new file mode 100644 index 00000000..a3dc0b18 --- /dev/null +++ b/src/beman/execution/store_receiver.cppm @@ -0,0 +1,11 @@ +module; +// src/beman/execution/store_receiver.cppm -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +export module beman.execution.detail.store_receiver; + +namespace beman::execution::detail { + export using beman::execution::detail::store_receiver; +} // namespace beman::execution::detail From b6686d316be21e9bdb410e8f8e21508b45c6777f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 21 Mar 2026 15:17:26 +0000 Subject: [PATCH 2/9] clang format --- include/beman/execution/detail/affine_on.hpp | 20 ++- .../execution/detail/sender_awaitable.hpp | 2 +- .../beman/execution/detail/store_receiver.hpp | 131 ++++++++---------- src/beman/execution/store_receiver.cppm | 2 +- 4 files changed, 72 insertions(+), 83 deletions(-) diff --git a/include/beman/execution/detail/affine_on.hpp b/include/beman/execution/detail/affine_on.hpp index 45f2bde1..49036490 100644 --- a/include/beman/execution/detail/affine_on.hpp +++ b/include/beman/execution/detail/affine_on.hpp @@ -155,17 +155,15 @@ struct affine_on_t : ::beman::execution::sender_adaptor_closure { constexpr child_tag_t t{}; return t.affine_on(::beman::execution::detail::forward_like(child), ev); } else { - return - ::beman::execution::detail::store_receiver( - ::beman::execution::detail::forward_like(child), - [](Child&& child, const auto& ev) { - return ::beman::execution::write_env( - ::beman::execution::schedule_from( - ::beman::execution::get_scheduler(ev), - ::beman::execution::write_env(::std::forward(child), ev)), - ::beman::execution::detail::affine_on_env(ev)) - ; - }); + return ::beman::execution::detail::store_receiver( + ::beman::execution::detail::forward_like(child), + [](Child&& child, const auto& ev) { + return ::beman::execution::write_env( + ::beman::execution::schedule_from( + ::beman::execution::get_scheduler(ev), + ::beman::execution::write_env(::std::forward(child), ev)), + ::beman::execution::detail::affine_on_env(ev)); + }); } } template diff --git a/include/beman/execution/detail/sender_awaitable.hpp b/include/beman/execution/detail/sender_awaitable.hpp index 16f43275..4a7571f0 100644 --- a/include/beman/execution/detail/sender_awaitable.hpp +++ b/include/beman/execution/detail/sender_awaitable.hpp @@ -48,7 +48,7 @@ import beman.execution.detail.unspecified_promise; namespace beman::execution::detail { template class sender_awaitable { - static inline constexpr bool enable_defence{true}; + inline static constexpr bool enable_defence{true}; struct unit {}; using value_type = ::beman::execution::detail::single_sender_value_type>; diff --git a/include/beman/execution/detail/store_receiver.hpp b/include/beman/execution/detail/store_receiver.hpp index c23d8462..0775faec 100644 --- a/include/beman/execution/detail/store_receiver.hpp +++ b/include/beman/execution/detail/store_receiver.hpp @@ -43,80 +43,71 @@ import beman.execution.detail.start; // ---------------------------------------------------------------------------- namespace beman::execution::detail { - struct store_receiver_t { - template <::beman::execution::receiver Rcvr> - struct receiver { - using receiver_concept = ::beman::execution::receiver_t; - Rcvr* rcvr; - template - auto set_value(Args&&... args) && noexcept -> void { - ::beman::execution::set_value(::std::move(*this->rcvr), ::std::forward(args)...); - } - template - auto set_error(Error&& error) && noexcept -> void { - ::beman::execution::set_error(::std::move(*this->rcvr), ::std::forward(error)); - } - auto set_stopped() && noexcept -> void { - ::beman::execution::set_stopped(::std::move(*this->rcvr)); - } - auto get_env() const noexcept { - return ::beman::execution::get_env(*this->rcvr); - } - }; - template <::beman::execution::sender Sndr, typename Trans, ::beman::execution::receiver Rcvr> - struct state { - using operation_state_concept = ::beman::execution::operation_state_t; - using env_t = ::beman::execution::env_of_t; - using state_t = ::beman::execution::connect_result_t< - decltype(::std::declval()(::std::declval(), ::std::declval())), - receiver>; - Rcvr rcvr; - state_t op_state; - template <::beman::execution::sender S, typename T, ::beman::execution::receiver R> - state(S&& sndr, T&& trans, R&& r) : - rcvr(::std::forward(::std::forward(r))) - ,op_state( - ::beman::execution::connect( - ::std::forward(trans)(::std::forward(sndr), ::beman::execution::get_env(this->rcvr)), - receiver{::std::addressof(this->rcvr)} - ) - ) - {} - auto start() & noexcept { - ::beman::execution::start(this->op_state); - } - }; - template <::beman::execution::sender Sndr, typename Trans> - struct sender { - using sender_concept = ::beman::execution::sender_t; - template - static consteval auto get_completion_signatures(Env&&... env) noexcept { - return ::beman::execution::get_completion_signatures<::std::remove_cvref_t, Env...>(); - } - ::std::remove_cvref_t sndr; - ::std::remove_cvref_t trans; - - template <::beman::execution::receiver Receiver> - auto connect(Receiver&& r) && { - static_assert(::beman::execution::operation_state>>); - return state>(::std::move(this->sndr), ::std::move(this->trans), ::std::forward(r)); - } - template <::beman::execution::receiver Receiver> - auto connect(Receiver&& r) const& { - static_assert(::beman::execution::operation_state>>); - return state>(this->sndr, this->trans, ::std::forward(r)); - } - }; - template <::beman::execution::sender Sndr, typename Trans> - auto operator()(Sndr&& sndr, Trans&& trans) const { - static_assert(::beman::execution::sender>); - return sender{::std::forward(sndr), ::std::forward(trans)}; +struct store_receiver_t { + template <::beman::execution::receiver Rcvr> + struct receiver { + using receiver_concept = ::beman::execution::receiver_t; + Rcvr* rcvr; + template + auto set_value(Args&&... args) && noexcept -> void { + ::beman::execution::set_value(::std::move(*this->rcvr), ::std::forward(args)...); + } + template + auto set_error(Error&& error) && noexcept -> void { + ::beman::execution::set_error(::std::move(*this->rcvr), ::std::forward(error)); } + auto set_stopped() && noexcept -> void { ::beman::execution::set_stopped(::std::move(*this->rcvr)); } + auto get_env() const noexcept { return ::beman::execution::get_env(*this->rcvr); } }; + template <::beman::execution::sender Sndr, typename Trans, ::beman::execution::receiver Rcvr> + struct state { + using operation_state_concept = ::beman::execution::operation_state_t; + using env_t = ::beman::execution::env_of_t; + using state_t = ::beman::execution::connect_result_t()( + ::std::declval(), ::std::declval())), + receiver>; + Rcvr rcvr; + state_t op_state; + template <::beman::execution::sender S, typename T, ::beman::execution::receiver R> + state(S&& sndr, T&& trans, R&& r) + : rcvr(::std::forward(::std::forward(r))), + op_state(::beman::execution::connect( + ::std::forward(trans)(::std::forward(sndr), ::beman::execution::get_env(this->rcvr)), + receiver{::std::addressof(this->rcvr)})) {} + auto start() & noexcept { ::beman::execution::start(this->op_state); } + }; + template <::beman::execution::sender Sndr, typename Trans> + struct sender { + using sender_concept = ::beman::execution::sender_t; + template + static consteval auto get_completion_signatures(Env&&... env) noexcept { + return ::beman::execution::get_completion_signatures<::std::remove_cvref_t, Env...>(); + } + ::std::remove_cvref_t sndr; + ::std::remove_cvref_t trans; - inline constexpr store_receiver_t store_receiver{}; -} + template <::beman::execution::receiver Receiver> + auto connect(Receiver&& r) && { + static_assert(::beman::execution::operation_state>>); + return state>( + ::std::move(this->sndr), ::std::move(this->trans), ::std::forward(r)); + } + template <::beman::execution::receiver Receiver> + auto connect(Receiver&& r) const& { + static_assert(::beman::execution::operation_state>>); + return state>( + this->sndr, this->trans, ::std::forward(r)); + } + }; + template <::beman::execution::sender Sndr, typename Trans> + auto operator()(Sndr&& sndr, Trans&& trans) const { + static_assert(::beman::execution::sender>); + return sender{::std::forward(sndr), ::std::forward(trans)}; + } +}; +inline constexpr store_receiver_t store_receiver{}; +} // namespace beman::execution::detail // ---------------------------------------------------------------------------- diff --git a/src/beman/execution/store_receiver.cppm b/src/beman/execution/store_receiver.cppm index a3dc0b18..bcfe2e7a 100644 --- a/src/beman/execution/store_receiver.cppm +++ b/src/beman/execution/store_receiver.cppm @@ -7,5 +7,5 @@ module; export module beman.execution.detail.store_receiver; namespace beman::execution::detail { - export using beman::execution::detail::store_receiver; +export using beman::execution::detail::store_receiver; } // namespace beman::execution::detail From 2699e53293a421cb9778dae64d0ee957514a3333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 21 Mar 2026 16:37:29 +0000 Subject: [PATCH 3/9] applied review feedback (partly from AI) --- include/beman/execution/detail/affine_on.hpp | 26 +++++----- .../beman/execution/detail/store_receiver.hpp | 7 +-- .../beman/execution/detail/unstoppable.hpp | 47 +++++++++++++++++++ src/beman/execution/CMakeLists.txt | 2 + src/beman/execution/execution.cppm | 1 + src/beman/execution/unstoppable.cppm | 11 +++++ 6 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 include/beman/execution/detail/unstoppable.hpp create mode 100644 src/beman/execution/unstoppable.cppm diff --git a/include/beman/execution/detail/affine_on.hpp b/include/beman/execution/detail/affine_on.hpp index 49036490..9c2210a5 100644 --- a/include/beman/execution/detail/affine_on.hpp +++ b/include/beman/execution/detail/affine_on.hpp @@ -24,7 +24,6 @@ import beman.execution.detail.get_completion_signatures; import beman.execution.detail.get_domain_early; import beman.execution.detail.get_scheduler; import beman.execution.detail.get_stop_token; -import beman.execution.detail.join_env; import beman.execution.detail.make_sender; import beman.execution.detail.never_stop_token; import beman.execution.detail.nested_sender_has_affine_on; @@ -40,6 +39,7 @@ import beman.execution.detail.set_value; import beman.execution.detail.store_receiver; import beman.execution.detail.tag_of_t; import beman.execution.detail.transform_sender; +import beman.execution.detail.unstoppable; import beman.execution.detail.write_env; #else #include @@ -48,7 +48,6 @@ import beman.execution.detail.write_env; #include #include #include -#include #include #include #include @@ -62,6 +61,7 @@ import beman.execution.detail.write_env; #include #include #include +#include #include #endif @@ -139,13 +139,13 @@ struct affine_on_t : ::beman::execution::sender_adaptor_closure { static auto transform_sender(Sender&& sender, const Env& ev) { static_assert(requires { { - ::beman::execution::get_completion_signatures< - decltype(::beman::execution::schedule(::beman::execution::get_scheduler(ev))), - decltype(::beman::execution::detail::join_env( - ::beman::execution::env{::beman::execution::prop{ - ::beman::execution::get_stop_token, ::beman::execution::never_stop_token{}, {}}}, - ev))>() - } -> ::std::same_as<::beman::execution::completion_signatures<::beman::execution::set_value_t()>>; + ::beman::execution::get_completion_signatures() + } //-dk:TODO -> + //::std::same_as<::beman::execution::completion_signatures<::beman::execution::set_value_t()>> + ; }); //[[maybe_unused]] auto& [tag, data, child] = sender; auto& child = sender.template get<2>(); @@ -158,11 +158,9 @@ struct affine_on_t : ::beman::execution::sender_adaptor_closure { return ::beman::execution::detail::store_receiver( ::beman::execution::detail::forward_like(child), [](Child&& child, const auto& ev) { - return ::beman::execution::write_env( - ::beman::execution::schedule_from( - ::beman::execution::get_scheduler(ev), - ::beman::execution::write_env(::std::forward(child), ev)), - ::beman::execution::detail::affine_on_env(ev)); + return ::beman::execution::unstoppable(::beman::execution::schedule_from( + ::beman::execution::get_scheduler(ev), + ::beman::execution::write_env(::std::forward(child), ev))); }); } } diff --git a/include/beman/execution/detail/store_receiver.hpp b/include/beman/execution/detail/store_receiver.hpp index 0775faec..df9068ae 100644 --- a/include/beman/execution/detail/store_receiver.hpp +++ b/include/beman/execution/detail/store_receiver.hpp @@ -1,4 +1,4 @@ -// include/beman/execution/detail/store_receiver.hpp -*-C++-*- +//-dk:TODO rRW/// include/beman/execution/detail/store_receiver.hpp -*-C++-*- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_STORE_RECEIVER @@ -70,7 +70,7 @@ struct store_receiver_t { state_t op_state; template <::beman::execution::sender S, typename T, ::beman::execution::receiver R> state(S&& sndr, T&& trans, R&& r) - : rcvr(::std::forward(::std::forward(r))), + : rcvr(::std::forward(r)), op_state(::beman::execution::connect( ::std::forward(trans)(::std::forward(sndr), ::beman::execution::get_env(this->rcvr)), receiver{::std::addressof(this->rcvr)})) {} @@ -81,7 +81,8 @@ struct store_receiver_t { using sender_concept = ::beman::execution::sender_t; template static consteval auto get_completion_signatures(Env&&... env) noexcept { - return ::beman::execution::get_completion_signatures<::std::remove_cvref_t, Env...>(); + return ::beman::execution:: + get_completion_signatures()(::std::declval())), Env...>(); } ::std::remove_cvref_t sndr; ::std::remove_cvref_t trans; diff --git a/include/beman/execution/detail/unstoppable.hpp b/include/beman/execution/detail/unstoppable.hpp new file mode 100644 index 00000000..32b87fa6 --- /dev/null +++ b/include/beman/execution/detail/unstoppable.hpp @@ -0,0 +1,47 @@ +// include/beman/execution/detail/unstoppable.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_UNSTOPPABLE +#define INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_UNSTOPPABLE + +#include +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#endif +#ifdef BEMAN_HAS_MODULES +import beman.execution.detail.get_stop_token; +import beman.execution.detail.never_stop_token; +import beman.execution.detail.prop; +import beman.execution.detail.sender; +import beman.execution.detail.write_env; +#else +#include +#include +#include +#include +#include +#endif + +// ---------------------------------------------------------------------------- + +namespace beman::execution::detail { +struct unstoppable_t { + template <::beman::execution::sender Sndr> + auto operator()(Sndr&& sndr) const { + return ::beman::execution::write_env( + ::std::forward(sndr), + ::beman::execution::prop{::beman::execution::get_stop_token, ::beman::execution::never_stop_token{}, {}}); + } +}; +} // namespace beman::execution::detail + +namespace beman::execution { +using unstoppable_t = ::beman::execution::detail::unstoppable_t; +inline constexpr ::beman::execution::unstoppable_t unstoppable{}; +} // namespace beman::execution + +// ---------------------------------------------------------------------------- + +#endif diff --git a/src/beman/execution/CMakeLists.txt b/src/beman/execution/CMakeLists.txt index 8585a84a..d426382c 100644 --- a/src/beman/execution/CMakeLists.txt +++ b/src/beman/execution/CMakeLists.txt @@ -189,6 +189,7 @@ target_sources( ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/transform_sender.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/type_list.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unspecified_promise.hpp + ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unstoppable.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/unstoppable_token.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/valid_completion_for.hpp ${PROJECT_SOURCE_DIR}/include/beman/execution/detail/valid_completion_signatures.hpp @@ -378,6 +379,7 @@ if(BEMAN_USE_MODULES) transform_sender.cppm type_list.cppm unspecified_promise.cppm + unstoppable.cppm unstoppable_token.cppm valid_completion_for.cppm valid_completion_signatures.cppm diff --git a/src/beman/execution/execution.cppm b/src/beman/execution/execution.cppm index 5d16e8e1..c14b20c6 100644 --- a/src/beman/execution/execution.cppm +++ b/src/beman/execution/execution.cppm @@ -83,6 +83,7 @@ import beman.execution.detail.inline_scheduler; // [stoptoken.concepts], stop token concepts export import beman.execution.detail.stoppable_token; +export import beman.execution.detail.unstoppable; export import beman.execution.detail.unstoppable_token; // [exec.recv], receivers diff --git a/src/beman/execution/unstoppable.cppm b/src/beman/execution/unstoppable.cppm new file mode 100644 index 00000000..c1de28a8 --- /dev/null +++ b/src/beman/execution/unstoppable.cppm @@ -0,0 +1,11 @@ +module; +// src/beman/execution/unstoppable.cppm -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +export module beman.execution.detail.unstoppable; + +namespace beman::execution { +export using beman::execution::unstoppable; +} // namespace beman::execution From 9194045fa0ce4cee72e3ddb6b0aaca9718576722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Fri, 3 Apr 2026 16:56:27 +0200 Subject: [PATCH 4/9] fixed a few issues - remove fatal error when homebrew isn't used - added a simple build procedure to the Makefile - added an example using suspend_never - added completion signatures to inline_scheduler - removed some gibberish from store_receiver.hpp --- Makefile | 18 ++++++++++- cmake/prelude.cmake | 2 +- examples/CMakeLists.txt | 3 +- examples/suspend_never.cpp | 32 +++++++++++++++++++ .../execution/detail/inline_scheduler.hpp | 4 +++ .../beman/execution/detail/store_receiver.hpp | 2 +- 6 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 examples/suspend_never.cpp diff --git a/Makefile b/Makefile index 0ad2487e..dcba1b5a 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,23 @@ endif # TODO: beman.execution.examples.modules # FIXME: beman.execution.execution-module.test beman.execution.stop-token-module.test -default: module +default: simple + +SIMPLE_BUILD = build/simple-$(shell uname -s) + +.PHONY: simple simple-configure simple-build simple-test + +simple: simple-test + +simple-config: + cmake -G Ninja -S . -B $(SIMPLE_BUILD) -DBEMAN_USE_MODULES=OFF -DCXXFLAGS= + +simple-build: simple-config + CXXFLAGS= cmake --build $(SIMPLE_BUILD) + +simple-test: simple-build + ctest --test-dir $(SIMPLE_BUILD) + all: $(SANITIZERS) diff --git a/cmake/prelude.cmake b/cmake/prelude.cmake index c0394e2a..574b8659 100644 --- a/cmake/prelude.cmake +++ b/cmake/prelude.cmake @@ -124,7 +124,7 @@ if( # gersemi: on else() message( - FATAL_ERROR + STATUS "File does NOT EXISTS! ${CMAKE_CXX_STDLIB_MODULES_JSON}" ) endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0f34e145..90a1d897 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -17,7 +17,7 @@ if(PROJECT_IS_TOP_LEVEL) enable_testing() endif() -set(TODO stop_token) #-dk:TODO +set(TODO stop_token) #-dk:TODO including that causes a linker error set(EXAMPLES allocator doc-just @@ -31,6 +31,7 @@ set(EXAMPLES sender-demo stackoverflow stopping + suspend_never when_all-cancel ) diff --git a/examples/suspend_never.cpp b/examples/suspend_never.cpp new file mode 100644 index 00000000..51b25d62 --- /dev/null +++ b/examples/suspend_never.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +namespace ex = beman::execution; + +void* operator new(std::size_t n) { + auto p = std::malloc(n); + std::cout << "global new(" << n << ")->" << p << "\n"; + return p; +} +void operator delete(void* ptr) noexcept { + std::cout << "global operator delete()" << ptr << "\n"; +} + +int main() { + struct resource: std::pmr::memory_resource { + void* do_allocate(std::size_t n, std::size_t) override { return std::malloc(n); } + void do_deallocate(void* p, std::size_t n, std::size_t) override { std::free(p); } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override{ + return this == &other; + } + } res{}; + + ex::sync_wait( + ex::write_env( + std::suspend_never(), + ex::env{ex::prop{ex::get_allocator, std::pmr::polymorphic_allocator(&res)}} + ) + ); +} diff --git a/include/beman/execution/detail/inline_scheduler.hpp b/include/beman/execution/detail/inline_scheduler.hpp index 0067fbf9..06f8df17 100644 --- a/include/beman/execution/detail/inline_scheduler.hpp +++ b/include/beman/execution/detail/inline_scheduler.hpp @@ -53,6 +53,10 @@ struct inline_scheduler { struct sender { using sender_concept = ::beman::execution::sender_t; using completion_signatures = ::beman::execution::completion_signatures<::beman::execution::set_value_t()>; + template + static consteval auto get_completion_signatures() noexcept -> completion_signatures { + return {}; + } static constexpr auto get_env() noexcept -> env { return {}; } diff --git a/include/beman/execution/detail/store_receiver.hpp b/include/beman/execution/detail/store_receiver.hpp index df9068ae..62e4c181 100644 --- a/include/beman/execution/detail/store_receiver.hpp +++ b/include/beman/execution/detail/store_receiver.hpp @@ -1,4 +1,4 @@ -//-dk:TODO rRW/// include/beman/execution/detail/store_receiver.hpp -*-C++-*- +// include/beman/execution/detail/store_receiver.hpp -*-C++-*- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef INCLUDED_INCLUDE_BEMAN_EXECUTION_DETAIL_STORE_RECEIVER From 2bc1c52adae64517c611f7ecfd298ab6dc977540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 9 Apr 2026 00:05:55 +0100 Subject: [PATCH 5/9] use modules in suspend_never example when enabled --- examples/suspend_never.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/suspend_never.cpp b/examples/suspend_never.cpp index 51b25d62..c34e23cc 100644 --- a/examples/suspend_never.cpp +++ b/examples/suspend_never.cpp @@ -1,7 +1,11 @@ -#include +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else #include #include #include +#endif +#include namespace ex = beman::execution; From 9653034826b5478fe35412ffe41779752913450e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 9 Apr 2026 00:24:16 +0100 Subject: [PATCH 6/9] formatting changes --- cmake/prelude.cmake | 5 +---- examples/suspend_never.cpp | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/cmake/prelude.cmake b/cmake/prelude.cmake index 574b8659..cdc58981 100644 --- a/cmake/prelude.cmake +++ b/cmake/prelude.cmake @@ -123,9 +123,6 @@ if( ) # gersemi: on else() - message( - STATUS - "File does NOT EXISTS! ${CMAKE_CXX_STDLIB_MODULES_JSON}" - ) + message(STATUS "File does NOT EXISTS! ${CMAKE_CXX_STDLIB_MODULES_JSON}") endif() endif() diff --git a/examples/suspend_never.cpp b/examples/suspend_never.cpp index c34e23cc..2ac00e44 100644 --- a/examples/suspend_never.cpp +++ b/examples/suspend_never.cpp @@ -1,6 +1,7 @@ #ifdef BEMAN_HAS_IMPORT_STD import std; #else +#include #include #include #include @@ -14,23 +15,15 @@ void* operator new(std::size_t n) { std::cout << "global new(" << n << ")->" << p << "\n"; return p; } -void operator delete(void* ptr) noexcept { - std::cout << "global operator delete()" << ptr << "\n"; -} +void operator delete(void* ptr) noexcept { std::cout << "global operator delete()" << ptr << "\n"; } int main() { - struct resource: std::pmr::memory_resource { - void* do_allocate(std::size_t n, std::size_t) override { return std::malloc(n); } - void do_deallocate(void* p, std::size_t n, std::size_t) override { std::free(p); } - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override{ - return this == &other; - } + struct resource : std::pmr::memory_resource { + void* do_allocate(std::size_t n, std::size_t) override { return std::malloc(n); } + void do_deallocate(void* p, std::size_t n, std::size_t) override { std::free(p); } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } } res{}; - ex::sync_wait( - ex::write_env( - std::suspend_never(), - ex::env{ex::prop{ex::get_allocator, std::pmr::polymorphic_allocator(&res)}} - ) - ); + ex::sync_wait(ex::write_env( + std::suspend_never(), ex::env{ex::prop{ex::get_allocator, std::pmr::polymorphic_allocator(&res)}})); } From 2adf88ed5ad5ddb89dfaf0f5286300ce236542bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 9 Apr 2026 07:15:13 +0100 Subject: [PATCH 7/9] another attempt at fixing suspend_never --- examples/CMakeLists.txt | 2 ++ examples/suspend_never.cpp | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 90a1d897..0bc1f5d3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -18,6 +18,8 @@ if(PROJECT_IS_TOP_LEVEL) endif() set(TODO stop_token) #-dk:TODO including that causes a linker error +set(TODO suspend_never) #-dk:TODO including that causes ASAN errors + set(EXAMPLES allocator doc-just diff --git a/examples/suspend_never.cpp b/examples/suspend_never.cpp index 2ac00e44..649898c0 100644 --- a/examples/suspend_never.cpp +++ b/examples/suspend_never.cpp @@ -15,13 +15,23 @@ void* operator new(std::size_t n) { std::cout << "global new(" << n << ")->" << p << "\n"; return p; } -void operator delete(void* ptr) noexcept { std::cout << "global operator delete()" << ptr << "\n"; } +void operator delete(void* ptr) noexcept { + std::cout << "global operator delete()" << ptr << "\n"; + std::free(ptr); +} int main() { struct resource : std::pmr::memory_resource { - void* do_allocate(std::size_t n, std::size_t) override { return std::malloc(n); } - void do_deallocate(void* p, std::size_t n, std::size_t) override { std::free(p); } - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } + void* do_allocate(std::size_t n, std::size_t a) override { + auto p{std::malloc(n)}; + std::cout << "resource::allocate(" << n << ", " << a << ") -> " << p << "\n"; + return p; + } + void do_deallocate(void* p, std::size_t n, std::size_t a) override { + std::cout << "resource::deallocate(" << p << ", " << n << ", " << a << ")\n"; + std::free(p); + } + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } } res{}; ex::sync_wait(ex::write_env( From b816383898523e4bb4cf166c79a6087e2b5ca0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 9 Apr 2026 23:05:46 +0100 Subject: [PATCH 8/9] disabling the suspend_never example for now --- examples/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0bc1f5d3..fcea24c0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -33,7 +33,6 @@ set(EXAMPLES sender-demo stackoverflow stopping - suspend_never when_all-cancel ) From 115fc86054ff3d6b08a5a2a1035251a0a827f8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Thu, 9 Apr 2026 23:07:32 +0100 Subject: [PATCH 9/9] removing trailing spaces --- examples/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index fcea24c0..c27226bc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,7 +19,7 @@ endif() set(TODO stop_token) #-dk:TODO including that causes a linker error set(TODO suspend_never) #-dk:TODO including that causes ASAN errors - + set(EXAMPLES allocator doc-just