From 487e6dabd8c30545e494be07cae6f2ab891b64e7 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Tue, 11 Aug 2026 01:51:16 +0200 Subject: [PATCH 1/2] Fix nvexec upon_stopped completions --- include/nvexec/stream/upon_stopped.cuh | 19 +++++---- test/nvexec/upon_stopped.cpp | 53 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/include/nvexec/stream/upon_stopped.cuh b/include/nvexec/stream/upon_stopped.cuh index cb7668c59..d4bc55478 100644 --- a/include/nvexec/stream/upon_stopped.cuh +++ b/include/nvexec/stream/upon_stopped.cuh @@ -106,7 +106,7 @@ namespace nv::execution::_strm status == cudaSuccess) { opstate_.defer_temp_storage_destruction(d_result); - opstate_.propagate_completion_signal(STDEXEC::set_value, *d_result); + opstate_.propagate_completion_signal(STDEXEC::set_value, std::move(*d_result)); } else { @@ -131,19 +131,24 @@ namespace nv::execution::_strm struct upon_stopped_sender : stream_sender_base { using sender_concept = STDEXEC::sender_tag; - using _set_error_t = completion_signatures; template using receiver_t = _upon_stopped::receiver; + template + using __error_completions_t = + __minvoke_q<__concat_completion_signatures_t, + __with_error_invoke_t<__mbind_front_q<__callable_error_t, upon_stopped_t>, + set_stopped_t, + Fun, + __copy_cvref_t, + Env...>, + completion_signatures>; + template using completion_signatures = __transform_completion_signatures_t< __completion_signatures_of_t<__copy_cvref_t, Env...>, - __with_error_invoke_t<__mbind_front_q<__callable_error_t, upon_stopped_t>, - set_stopped_t, - Fun, - __copy_cvref_t, - Env...>, + __error_completions_t, __cmplsigs::__default_set_value, __cmplsigs::__default_set_error, __set_value_from_t>; diff --git a/test/nvexec/upon_stopped.cpp b/test/nvexec/upon_stopped.cpp index 09fca2890..dad905ba9 100644 --- a/test/nvexec/upon_stopped.cpp +++ b/test/nvexec/upon_stopped.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include "common.cuh" #include "nvexec/stream_context.cuh" @@ -10,6 +12,45 @@ using nvexec::is_on_gpu; namespace { + struct move_only_result + { + STDEXEC_ATTRIBUTE(host, device) + explicit move_only_result(int value) noexcept + : value_(value) + {} + + STDEXEC_ATTRIBUTE(host, device) + move_only_result(move_only_result&& other) noexcept + : value_(other.value_) + { + other.value_ = 0; + } + + move_only_result(move_only_result const &) = delete; + + STDEXEC_ATTRIBUTE(host, device) + ~move_only_result() = default; + + STDEXEC_ATTRIBUTE(host, device) + auto value() const noexcept -> int + { + return value_; + } + + private: + int value_; + }; + + TEST_CASE("nvexec upon_stopped advertises CUDA launch errors", + "[cuda][stream][adaptors][upon_stopped]") + { + auto fun = []() noexcept {}; + using sender_t = nvexec::_strm::upon_stopped_sender, + decltype(fun)>; + sender_t snd{a_sender_of{}, std::move(fun)}; + + check_err_types>(snd); + } TEST_CASE("nvexec upon_stopped returns a sender", "[cuda][stream][adaptors][upon_stopped]") { @@ -41,4 +82,16 @@ namespace REQUIRE(flags_storage.all_set_once()); } + + TEST_CASE("nvexec upon_stopped moves its result", "[cuda][stream][adaptors][upon_stopped]") + { + nvexec::stream_context stream_ctx{}; + + auto snd = ex::just_stopped() | ex::continues_on(stream_ctx.get_scheduler()) + | ex::upon_stopped([] { return move_only_result{42}; }); + + auto [result] = STDEXEC::sync_wait(std::move(snd)).value(); + + REQUIRE(result.value() == 42); + } } // namespace From 7344c8f228230d605d962b2d95a3023c5a59ff28 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Wed, 12 Aug 2026 01:45:26 +0200 Subject: [PATCH 2/2] Fix nvexec upon_stopped callable forwarding --- include/nvexec/stream/upon_stopped.cuh | 2 +- test/nvexec/upon_stopped.cpp | 31 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/nvexec/stream/upon_stopped.cuh b/include/nvexec/stream/upon_stopped.cuh index d4bc55478..4505e3eea 100644 --- a/include/nvexec/stream/upon_stopped.cuh +++ b/include/nvexec/stream/upon_stopped.cuh @@ -168,7 +168,7 @@ namespace nv::execution::_strm static_cast(self).sndr_, static_cast(rcvr), [&](_strm::opstate_base& stream_provider) -> receiver_t - { return receiver_t(self.fun_, stream_provider); }); + { return receiver_t(static_cast(self).fun_, stream_provider); }); } STDEXEC_EXPLICIT_THIS_END(connect) diff --git a/test/nvexec/upon_stopped.cpp b/test/nvexec/upon_stopped.cpp index dad905ba9..c27a41c36 100644 --- a/test/nvexec/upon_stopped.cpp +++ b/test/nvexec/upon_stopped.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "common.cuh" #include "nvexec/stream_context.cuh" @@ -12,6 +14,23 @@ using nvexec::is_on_gpu; namespace { + struct move_only_stopped_handler + { + move_only_stopped_handler() = default; + move_only_stopped_handler(move_only_stopped_handler const &) = delete; + + STDEXEC_ATTRIBUTE(host, device) + move_only_stopped_handler(move_only_stopped_handler &&) = default; + + STDEXEC_ATTRIBUTE(host, device) auto operator()() const -> int + { + return 42; + } + }; + + static_assert(std::is_trivially_copyable_v); + static_assert(!std::is_copy_constructible_v); + struct move_only_result { STDEXEC_ATTRIBUTE(host, device) @@ -83,6 +102,18 @@ namespace REQUIRE(flags_storage.all_set_once()); } + TEST_CASE("nvexec upon_stopped supports move-only function objects", + "[cuda][stream][adaptors][upon_stopped]") + { + nvexec::stream_context stream_ctx{}; + + auto snd = ex::just_stopped() | ex::continues_on(stream_ctx.get_scheduler()) + | ex::upon_stopped(move_only_stopped_handler{}); + auto const [result] = STDEXEC::sync_wait(std::move(snd)).value(); + + REQUIRE(result == 42); + } + TEST_CASE("nvexec upon_stopped moves its result", "[cuda][stream][adaptors][upon_stopped]") { nvexec::stream_context stream_ctx{};