From 0ab6dd5cbf215994c902d61b835322c97a1b8313 Mon Sep 17 00:00:00 2001 From: Cra3z Date: Fri, 17 Jul 2026 14:28:54 +0800 Subject: [PATCH 1/2] Fix issue where lvalue scope-tokens cannot be used as arguments for `spawn` and `spawn_future` --- include/beman/execution/detail/spawn.hpp | 13 ++++++++----- include/beman/execution/detail/spawn_future.hpp | 10 +++++----- tests/beman/execution/exec-spawn-future.test.cpp | 4 ++++ tests/beman/execution/exec-spawn.test.cpp | 3 +++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/include/beman/execution/detail/spawn.hpp b/include/beman/execution/detail/spawn.hpp index b6ee4913..59264035 100644 --- a/include/beman/execution/detail/spawn.hpp +++ b/include/beman/execution/detail/spawn.hpp @@ -9,12 +9,14 @@ 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; +import beman.execution.detail.queryable; import beman.execution.detail.receiver; import beman.execution.detail.scope_token; import beman.execution.detail.sender; @@ -25,6 +27,7 @@ import beman.execution.detail.write_env; #include #include #include +#include #include #include #include @@ -53,11 +56,11 @@ struct spawn_t { template struct state : state_base { using op_t = ::beman::execution::connect_result_t; - using assoc_t = ::std::remove_cvref_t().try_associate())>; + using assoc_t = ::std::remove_cvref_t().try_associate())>; using alloc_t = typename ::std::allocator_traits::template rebind_alloc; using traits_t = ::std::allocator_traits; - state(Alloc a, Sndr&& sndr, Tok tok) + state(Alloc a, Sndr&& sndr, const Tok& tok) : alloc(a), op(::beman::execution::connect(::std::forward(sndr), receiver{this})), assoc(tok.try_associate()) { @@ -83,7 +86,7 @@ struct spawn_t { }; template <::beman::execution::sender Sender, ::beman::execution::scope_token Token, typename Env> - auto operator()(Sender&& sender, Token&& tok, Env&& env) const { + auto operator()(Sender&& sender, const Token& tok, Env&& env) const { auto new_sender{tok.wrap(::std::forward(sender))}; auto [all, senv] = ::beman::execution::detail::spawn_get_allocator(new_sender, env); @@ -96,8 +99,8 @@ struct spawn_t { traits_t::construct(alloc, op, all, ::beman::execution::write_env(::std::move(new_sender), senv), tok); } template <::beman::execution::sender Sender, ::beman::execution::scope_token Token> - auto operator()(Sender&& sender, Token&& token) const { - return (*this)(::std::forward(sender), ::std::forward(token), ::beman::execution::env<>{}); + auto operator()(Sender&& sender, const Token& token) const { + return (*this)(::std::forward(sender), token, ::beman::execution::env<>{}); } }; } // namespace beman::execution::detail diff --git a/include/beman/execution/detail/spawn_future.hpp b/include/beman/execution/detail/spawn_future.hpp index 1adc1951..af2e02ec 100644 --- a/include/beman/execution/detail/spawn_future.hpp +++ b/include/beman/execution/detail/spawn_future.hpp @@ -152,7 +152,7 @@ template > { using alloc_t = typename ::std::allocator_traits::template rebind_alloc; - using assoc_t = ::std::remove_cvref_t().try_associate())>; + using assoc_t = ::std::remove_cvref_t().try_associate())>; using traits_t = ::std::allocator_traits; using spawned_sender_t = ::beman::execution::detail::future_spawned_sender; using sigs_t = ::beman::execution::detail::spawn_future_sigs; @@ -162,7 +162,7 @@ struct spawn_future_state using op_t = ::beman::execution::connect_result_t; template <::beman::execution::sender S> - spawn_future_state(auto a, S&& s, Token tok, Env env) + spawn_future_state(auto a, S&& s, const Token& tok, Env env) : alloc(::std::move(a)), op(::beman::execution::write_env( ::beman::execution::detail::stop_when(::std::forward(s), source.get_token()), env), @@ -248,7 +248,7 @@ class spawn_future_t { public: template <::beman::execution::sender Sndr, ::beman::execution::scope_token Tok, typename Ev> requires ::beman::execution::detail::queryable<::std::remove_cvref_t> - auto operator()(Sndr&& sndr, Tok&& tok, Ev&& ev) const { + auto operator()(Sndr&& sndr, const Tok& tok, Ev&& ev) const { //-dk:TODO why decltype(auto) instead of auto? auto make{[&]() -> decltype(auto) { return tok.wrap(::std::forward(sndr)); }}; using sndr_t = decltype(make()); @@ -271,8 +271,8 @@ class spawn_future_t { return ::beman::execution::detail::make_sender(*this, ::std::unique_ptr{op}); } template <::beman::execution::sender Sndr, ::beman::execution::scope_token Tok> - auto operator()(Sndr&& sndr, Tok&& tok) const { - return (*this)(::std::forward(sndr), ::std::forward(tok), ::beman::execution::env<>{}); + auto operator()(Sndr&& sndr, const Tok& tok) const { + return (*this)(::std::forward(sndr), tok, ::beman::execution::env<>{}); } private: diff --git a/tests/beman/execution/exec-spawn-future.test.cpp b/tests/beman/execution/exec-spawn-future.test.cpp index 36aaf96d..eb59b15d 100644 --- a/tests/beman/execution/exec-spawn-future.test.cpp +++ b/tests/beman/execution/exec-spawn-future.test.cpp @@ -473,6 +473,10 @@ TEST(exec_spawn_future) { test_spawn_future_interface(sender{}, token{}); + token lvalue{}; + test_spawn_future_interface(sender{}, lvalue); + test_spawn_future_interface(sender{}, std::as_const(lvalue)); + test_state_base(); test_receiver(); diff --git a/tests/beman/execution/exec-spawn.test.cpp b/tests/beman/execution/exec-spawn.test.cpp index 5b51323a..d326581f 100644 --- a/tests/beman/execution/exec-spawn.test.cpp +++ b/tests/beman/execution/exec-spawn.test.cpp @@ -203,6 +203,9 @@ TEST(exec_spawn) { test_overload(sender{}, token{}, env{}); test_overload(sender{}, token{}, env{}); test_overload(sender{}, token{}, env{}); + token lvalue{}; + test_overload(sender{}, lvalue, env{}); + test_overload(sender{}, std::as_const(lvalue), env{}); test_spawn_state_base(); test_spawn_receiver(); From 4ae10ef1ebc4deb01172d3aed269fc7dd2aed080 Mon Sep 17 00:00:00 2001 From: Cra3z Date: Fri, 17 Jul 2026 14:57:09 +0800 Subject: [PATCH 2/2] Exception safety on `spawn` allocation failure --- include/beman/execution/detail/spawn.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/beman/execution/detail/spawn.hpp b/include/beman/execution/detail/spawn.hpp index 59264035..0f54d91f 100644 --- a/include/beman/execution/detail/spawn.hpp +++ b/include/beman/execution/detail/spawn.hpp @@ -86,6 +86,7 @@ struct spawn_t { }; template <::beman::execution::sender Sender, ::beman::execution::scope_token Token, typename Env> + requires ::beman::execution::detail::queryable<::std::remove_cvref_t> auto operator()(Sender&& sender, const Token& tok, Env&& env) const { auto new_sender{tok.wrap(::std::forward(sender))}; auto [all, senv] = ::beman::execution::detail::spawn_get_allocator(new_sender, env); @@ -96,7 +97,12 @@ struct spawn_t { using traits_t = ::std::allocator_traits; alloc_t alloc(all); state_t* op{traits_t::allocate(alloc, 1u)}; - traits_t::construct(alloc, op, all, ::beman::execution::write_env(::std::move(new_sender), senv), tok); + try { + traits_t::construct(alloc, op, all, ::beman::execution::write_env(::std::move(new_sender), senv), tok); + } catch (...) { + traits_t::deallocate(alloc, op, 1u); + throw; + } } template <::beman::execution::sender Sender, ::beman::execution::scope_token Token> auto operator()(Sender&& sender, const Token& token) const {