Skip to content

Commit ca11a04

Browse files
Fix nvexec upon_stopped completion delivery (#2183)
* Fix nvexec upon_stopped completions * working around nvhpc ICE and warnings --------- Co-authored-by: Eric Niebler <eniebler@nvidia.com>
1 parent bad1a73 commit ca11a04

6 files changed

Lines changed: 102 additions & 25 deletions

File tree

examples/nvexec/maxwell/common.cuh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
# include "nvexec/detail/throw_on_cuda_error.cuh"
4040
#endif
4141

42+
STDEXEC_PRAGMA_PUSH()
43+
STDEXEC_PRAGMA_IGNORE_EDG(is_constant_evaluated_in_nonconstexpr_context)
44+
4245
struct deleter_t
4346
{
4447
bool on_gpu{};
@@ -538,3 +541,5 @@ auto value(std::map<std::string_view, std::size_t> const &params,
538541
}
539542
return default_value;
540543
}
544+
545+
STDEXEC_PRAGMA_POP()

examples/nvexec/maxwell/stdpar.cuh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131

3232
# include <algorithm>
3333

34+
STDEXEC_PRAGMA_PUSH()
35+
STDEXEC_PRAGMA_IGNORE_EDG(is_constant_evaluated_in_nonconstexpr_context)
36+
3437
template <class Policy>
3538
auto is_gpu_policy([[maybe_unused]] Policy&& policy) -> bool
3639
{
@@ -81,4 +84,6 @@ void run_stdpar(float dt,
8184
});
8285
}
8386

87+
STDEXEC_PRAGMA_POP()
88+
8489
#endif // !STDEXEC_NO_STDCPP_PARALLEL_ALGORITHMS()

include/exec/static_thread_pool.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ import stdexec;
6262
#include "sequence/iterate.hpp"
6363
#include "sequence_senders.hpp"
6464

65+
STDEXEC_PRAGMA_PUSH()
66+
STDEXEC_PRAGMA_IGNORE_EDG(is_constant_evaluated_in_nonconstexpr_context)
67+
6568
namespace experimental::execution
6669
{
6770
struct bwos_params
@@ -1847,3 +1850,5 @@ namespace experimental::execution
18471850

18481851
STDEXEC_MODULE_EXPORT
18491852
namespace exec = experimental::execution;
1853+
1854+
STDEXEC_PRAGMA_POP()

include/nvexec/stream/common.cuh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,13 @@ namespace nv::execution
10131013
template <class Fun, class... Args>
10141014
using __f = STDEXEC::__msize_t<_sizeof_v<STDEXEC::__call_result_t<Fun, Args...>>>;
10151015
};
1016+
1017+
using _cuda_error_completion_t =
1018+
STDEXEC::completion_signatures<STDEXEC::set_error_t(cudaError_t)>;
1019+
1020+
template <class _NoExcept>
1021+
using _cuda_error_completion_unless_t =
1022+
STDEXEC::__if<_NoExcept, STDEXEC::completion_signatures<>, _cuda_error_completion_t>;
10161023
} // namespace nv::execution
10171024

10181025
namespace nvexec = nv::execution;

include/nvexec/stream/upon_stopped.cuh

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@
1919
#pragma once
2020

2121
#include "../../stdexec/execution.hpp"
22-
#include <cstddef>
23-
#include <exception>
24-
#include <type_traits>
2522

26-
#include <cuda/std/utility>
23+
#include "../../exec/completion_signatures.hpp"
2724

2825
#include "common.cuh"
2926

27+
#include <cuda/std/utility>
28+
29+
#include <cstddef>
30+
#include <type_traits>
31+
3032
STDEXEC_PRAGMA_PUSH()
3133
STDEXEC_PRAGMA_IGNORE_EDG(cuda_compile)
3234

@@ -106,7 +108,7 @@ namespace nv::execution::_strm
106108
status == cudaSuccess)
107109
{
108110
opstate_.defer_temp_storage_destruction(d_result);
109-
opstate_.propagate_completion_signal(STDEXEC::set_value, *d_result);
111+
opstate_.propagate_completion_signal(STDEXEC::set_value, std::move(*d_result));
110112
}
111113
else
112114
{
@@ -125,28 +127,24 @@ namespace nv::execution::_strm
125127
Fun fun_;
126128
_strm::opstate_base<Receiver>& opstate_;
127129
};
130+
131+
template <class Fun>
132+
consteval auto _get_completions_fun() noexcept
133+
{
134+
return []() noexcept
135+
{
136+
return __set_value_from_t<Fun>();
137+
};
138+
}
128139
} // namespace _upon_stopped
129140

130141
template <class Sender, class Fun>
131142
struct upon_stopped_sender : stream_sender_base
132143
{
133144
using sender_concept = STDEXEC::sender_tag;
134-
using _set_error_t = completion_signatures<set_error_t(std::exception_ptr)>;
135145

136146
template <class Receiver>
137-
using receiver_t = _upon_stopped::receiver<Receiver, Fun>;
138-
139-
template <class Self, class... Env>
140-
using completion_signatures = __transform_completion_signatures_t<
141-
__completion_signatures_of_t<__copy_cvref_t<Self, Sender>, Env...>,
142-
__with_error_invoke_t<__mbind_front_q<__callable_error_t, upon_stopped_t>,
143-
set_stopped_t,
144-
Fun,
145-
__copy_cvref_t<Self, Sender>,
146-
Env...>,
147-
__cmplsigs::__default_set_value,
148-
__cmplsigs::__default_set_error,
149-
__set_value_from_t<Fun>>;
147+
using _receiver_t = _upon_stopped::receiver<Receiver, Fun>;
150148

151149
explicit upon_stopped_sender(Sender sndr, Fun fun)
152150
noexcept(__nothrow_move_constructible<Sender, Fun>)
@@ -155,22 +153,26 @@ namespace nv::execution::_strm
155153
{}
156154

157155
template <__decays_to<upon_stopped_sender> Self, STDEXEC::receiver Receiver>
158-
requires receiver_of<Receiver, completion_signatures<Self, env_of_t<Receiver>>>
159156
STDEXEC_EXPLICIT_THIS_BEGIN(auto connect)(this Self&& self, Receiver rcvr)
160-
-> stream_opstate_t<__copy_cvref_t<Self, Sender>, receiver_t<Receiver>, Receiver>
157+
-> stream_opstate_t<__copy_cvref_t<Self, Sender>, _receiver_t<Receiver>, Receiver>
161158
{
162159
return stream_opstate<__copy_cvref_t<Self, Sender>>(
163160
static_cast<Self&&>(self).sndr_,
164161
static_cast<Receiver&&>(rcvr),
165-
[&](_strm::opstate_base<Receiver>& stream_provider) -> receiver_t<Receiver>
166-
{ return receiver_t<Receiver>(self.fun_, stream_provider); });
162+
[&](_strm::opstate_base<Receiver>& stream_provider) -> _receiver_t<Receiver>
163+
{ return _receiver_t<Receiver>(self.fun_, stream_provider); });
167164
}
168165
STDEXEC_EXPLICIT_THIS_END(connect)
169166

170167
template <__decays_to<upon_stopped_sender> Self, class... Env>
171-
static consteval auto get_completion_signatures() -> completion_signatures<Self, Env...>
168+
static consteval auto get_completion_signatures()
172169
{
173-
return {};
170+
return exec::transform_completion_signatures(
171+
STDEXEC::get_completion_signatures<__copy_cvref_t<Self, Sender>, Env...>(),
172+
{},
173+
{},
174+
_upon_stopped::_get_completions_fun<Fun>(),
175+
_cuda_error_completion_t());
174176
}
175177

176178
auto get_env() const noexcept -> stream_sender_attrs<Sender>

test/nvexec/upon_stopped.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include <stdexec/execution.hpp>
22
#include <test_common/catch2.hpp>
3+
#include <test_common/senders.hpp>
4+
#include <test_common/type_helpers.hpp>
35

46
#include "common.cuh"
57
#include "nvexec/stream_context.cuh"
@@ -10,6 +12,45 @@ using nvexec::is_on_gpu;
1012

1113
namespace
1214
{
15+
struct move_only_result
16+
{
17+
STDEXEC_ATTRIBUTE(host, device)
18+
explicit move_only_result(int value) noexcept
19+
: value_(value)
20+
{}
21+
22+
STDEXEC_ATTRIBUTE(host, device)
23+
move_only_result(move_only_result&& other) noexcept
24+
: value_(other.value_)
25+
{
26+
other.value_ = 0;
27+
}
28+
29+
move_only_result(move_only_result const &) = delete;
30+
31+
STDEXEC_ATTRIBUTE(host, device)
32+
~move_only_result() = default;
33+
34+
STDEXEC_ATTRIBUTE(host, device)
35+
auto value() const noexcept -> int
36+
{
37+
return value_;
38+
}
39+
40+
private:
41+
int value_;
42+
};
43+
44+
TEST_CASE("nvexec upon_stopped advertises CUDA launch errors",
45+
"[cuda][stream][adaptors][upon_stopped]")
46+
{
47+
auto fun = []() noexcept {};
48+
using sender_t =
49+
nvexec::_strm::upon_stopped_sender<a_sender_of<ex::set_stopped_t()>, decltype(fun)>;
50+
sender_t snd{a_sender_of<ex::set_stopped_t()>{}, std::move(fun)};
51+
52+
check_err_types<ex::__mset<cudaError_t>>(snd);
53+
}
1354

1455
TEST_CASE("nvexec upon_stopped returns a sender", "[cuda][stream][adaptors][upon_stopped]")
1556
{
@@ -41,4 +82,16 @@ namespace
4182

4283
REQUIRE(flags_storage.all_set_once());
4384
}
85+
86+
TEST_CASE("nvexec upon_stopped moves its result", "[cuda][stream][adaptors][upon_stopped]")
87+
{
88+
nvexec::stream_context stream_ctx{};
89+
90+
auto snd = ex::just_stopped() | ex::continues_on(stream_ctx.get_scheduler())
91+
| ex::upon_stopped([] { return move_only_result{42}; });
92+
93+
auto [result] = STDEXEC::sync_wait(std::move(snd)).value();
94+
95+
REQUIRE(result.value() == 42);
96+
}
4497
} // namespace

0 commit comments

Comments
 (0)