Skip to content

Commit 131457b

Browse files
committed
added spawn_future receiver
1 parent 87bd8ec commit 131457b

File tree

2 files changed

+122
-3
lines changed

2 files changed

+122
-3
lines changed

include/beman/execution/detail/spawn_future.hpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include <beman/execution/detail/queryable.hpp>
1111
#include <beman/execution/detail/as_tuple.hpp>
1212
#include <beman/execution/detail/meta_unique.hpp>
13+
#include <beman/execution/detail/receiver.hpp>
14+
#include <beman/execution/detail/set_error.hpp>
15+
#include <beman/execution/detail/set_stopped.hpp>
16+
#include <beman/execution/detail/set_value.hpp>
1317
#include <utility>
1418
#include <variant>
1519
#include <type_traits>
@@ -28,9 +32,10 @@ namespace beman::execution::detail {
2832
template <typename Completions> struct spawn_future_state_base;
2933
template <typename... Sigs>
3034
struct spawn_future_state_base<::beman::execution::completion_signatures<Sigs...>> {
35+
static constexpr bool has_non_throwing_args_copy = (true && ... && non_throwing_args_copy_v<Sigs>);
3136
using variant_t = ::beman::execution::detail::meta::unique<
3237
::std::conditional_t<
33-
(true && ... && non_throwing_args_copy_v<Sigs>),
38+
has_non_throwing_args_copy,
3439
::std::variant<::std::monostate, ::beman::execution::detail::as_tuple_t<Sigs>...>,
3540
::std::variant<::std::monostate, ::std::tuple<::beman::execution::set_error_t, ::std::exception_ptr>, ::beman::execution::detail::as_tuple_t<Sigs>...>
3641
>
@@ -41,6 +46,39 @@ namespace beman::execution::detail {
4146
virtual auto complete() noexcept -> void = 0;
4247
};
4348

49+
template <typename Completions>
50+
struct spawn_future_receiver {
51+
using receiver_concept = ::beman::execution::receiver_t;
52+
using state_t = ::beman::execution::detail::spawn_future_state_base<Completions>;
53+
54+
state_t* state{};
55+
56+
template <typename... A>
57+
auto set_value(A&&... a) && noexcept -> void {
58+
this->set_complete<::beman::execution::set_value_t>(::std::forward<A>(a)...);
59+
}
60+
template <typename E>
61+
auto set_error(E&& e) && noexcept -> void {
62+
this->set_complete<::beman::execution::set_error_t>(::std::forward<E>(e));
63+
}
64+
auto set_stopped() && noexcept -> void {
65+
this->set_complete<::beman::execution::set_stopped_t>();
66+
}
67+
68+
template <typename Tag, typename... T>
69+
auto set_complete(T&&... t) noexcept {
70+
try {
71+
this->state->result.template emplace<::beman::execution::detail::decayed_tuple<Tag, T...>>(Tag(), ::std::forward<T>(t)...);
72+
}
73+
catch (...) {
74+
if constexpr (!state_t::has_non_throwing_args_copy) {
75+
this->state->result.template emplace<::std::tuple<::beman::execution::set_error_t, ::std::exception_ptr>>(::beman::execution::set_error_t{}, ::std::current_exception());
76+
}
77+
}
78+
this->state->complete();
79+
}
80+
};
81+
4482
class spawn_future_t {
4583
public:
4684
template <::beman::execution::sender Sndr, ::beman::execution::async_scope_token Tok, typename Env>

tests/beman/execution/exec-spawn-future.test.cpp

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <beman/execution/detail/queryable.hpp>
66
#include <beman/execution/detail/sender.hpp>
77
#include <beman/execution/detail/async_scope_token.hpp>
8+
#include <beman/execution/detail/receiver.hpp>
89
#include <test/execution.hpp>
910
#include <concepts>
1011

@@ -37,9 +38,10 @@ namespace
3738
static_assert(test_std::async_scope_token<token<true>>);
3839
static_assert(not test_std::async_scope_token<token<false>>);
3940

41+
struct exception { int value; };
4042
struct throws {
4143
throws() = default;
42-
throws(throws&&) noexcept(false) = default;
44+
throws(throws&&) noexcept(false) { throw exception{42}; }
4345
};
4446
static_assert(!std::is_nothrow_constructible_v<std::decay_t<throws>, throws>);
4547

@@ -53,7 +55,8 @@ namespace
5355

5456
template <typename Completions>
5557
struct state_base: test_detail::spawn_future_state_base<Completions> {
56-
auto complete() noexcept -> void override {}
58+
bool called{false};
59+
auto complete() noexcept -> void override { this->called = true; }
5760
};
5861
auto test_state_base() {
5962
static_assert(noexcept(std::declval<test_detail::spawn_future_state_base<test_std::completion_signatures<>>&>().complete()));
@@ -81,6 +84,83 @@ namespace
8184
[[maybe_unused]] state4_t b4;
8285
static_assert(std::same_as<std::variant<std::monostate, std::tuple<test_std::set_error_t, std::exception_ptr>, std::tuple<test_std::set_value_t, throws>>, typename state4_t::variant_t>);
8386
static_assert(std::same_as<state4_t::variant_t, decltype(b4.result)>);
87+
88+
using state5_t = state_base<test_std::completion_signatures<test_std::set_stopped_t()>>;
89+
[[maybe_unused]] state5_t b5;
90+
static_assert(std::same_as<std::variant<std::monostate, std::tuple<test_std::set_stopped_t>>, typename state5_t::variant_t>);
91+
static_assert(std::same_as<state5_t::variant_t, decltype(b5.result)>);
92+
}
93+
94+
auto test_receiver() {
95+
{
96+
using c_t = test_std::completion_signatures<test_std::set_stopped_t()>;
97+
state_base<c_t> state{};
98+
ASSERT(state.called == false);
99+
ASSERT(std::holds_alternative<std::monostate>(state.result));
100+
static_assert(test_std::receiver<test_detail::spawn_future_receiver<c_t>>);
101+
[[maybe_unused]] test_detail::spawn_future_receiver<c_t> r0{&state};
102+
[](auto&& r){ static_assert(not requires{ r.set_stopped(); }); }(r0);
103+
static_assert(noexcept(std::move(r0).set_stopped()));
104+
std::move(r0).set_stopped();
105+
ASSERT(state.called == true);
106+
ASSERT((std::holds_alternative<std::tuple<test_std::set_stopped_t>>(state.result)));
107+
}
108+
109+
{
110+
using c_t = test_std::completion_signatures<test_std::set_error_t(int)>;
111+
state_base<c_t> state{};
112+
ASSERT(state.called == false);
113+
ASSERT(std::holds_alternative<std::monostate>(state.result));
114+
static_assert(test_std::receiver<test_detail::spawn_future_receiver<c_t>>);
115+
[[maybe_unused]] test_detail::spawn_future_receiver<c_t> r0{&state};
116+
[](auto&& r){ static_assert(not requires{ r.set_error(17); }); }(r0);
117+
static_assert(noexcept(std::move(r0).set_error(17)));
118+
std::move(r0).set_error(17);
119+
ASSERT(state.called == true);
120+
ASSERT((std::holds_alternative<std::tuple<test_std::set_error_t, int>>(state.result)));
121+
ASSERT((std::get<1>(std::get<std::tuple<test_std::set_error_t, int>>(state.result)) == 17));
122+
}
123+
124+
{
125+
using c_t = test_std::completion_signatures<test_std::set_value_t(int, bool, char)>;
126+
state_base<c_t> state{};
127+
ASSERT(state.called == false);
128+
ASSERT(std::holds_alternative<std::monostate>(state.result));
129+
static_assert(test_std::receiver<test_detail::spawn_future_receiver<c_t>>);
130+
[[maybe_unused]] test_detail::spawn_future_receiver<c_t> r0{&state};
131+
[](auto&& r){ static_assert(not requires{ r.set_value(17, true, 'x'); }); }(r0);
132+
static_assert(noexcept(std::move(r0).set_value(17, true, 'x')));
133+
std::move(r0).set_value(17, true, 'x');
134+
ASSERT(state.called == true);
135+
ASSERT((std::holds_alternative<std::tuple<test_std::set_value_t, int, bool, char>>(state.result)));
136+
ASSERT((std::get<1>(std::get<std::tuple<test_std::set_value_t, int, bool, char>>(state.result)) == 17));
137+
ASSERT((std::get<2>(std::get<std::tuple<test_std::set_value_t, int, bool, char>>(state.result)) == true));
138+
ASSERT((std::get<3>(std::get<std::tuple<test_std::set_value_t, int, bool, char>>(state.result)) == 'x'));
139+
}
140+
141+
{
142+
using c_t = test_std::completion_signatures<test_std::set_value_t(int, throws, char)>;
143+
state_base<c_t> state{};
144+
ASSERT(state.called == false);
145+
ASSERT(std::holds_alternative<std::monostate>(state.result));
146+
static_assert(test_std::receiver<test_detail::spawn_future_receiver<c_t>>);
147+
[[maybe_unused]] test_detail::spawn_future_receiver<c_t> r0{&state};
148+
[](auto&& r){ static_assert(not requires{ r.set_value(17, throws(), 'x'); }); }(r0);
149+
static_assert(noexcept(std::move(r0).set_value(17, throws(), 'x')));
150+
std::move(r0).set_value(17, throws(), 'x');
151+
ASSERT(state.called == true);
152+
ASSERT((std::holds_alternative<std::tuple<test_std::set_error_t, std::exception_ptr>>(state.result)));
153+
try {
154+
std::rethrow_exception(std::get<1>(std::get<std::tuple<test_std::set_error_t, std::exception_ptr>>(state.result)));
155+
ASSERT(nullptr == "not reached");
156+
}
157+
catch (exception const& ex) {
158+
ASSERT(ex.value == 42);
159+
}
160+
catch (...) {
161+
ASSERT(nullptr == "not reached");
162+
}
163+
}
84164
}
85165
}
86166

@@ -92,4 +172,5 @@ TEST(exec_spawn_future) {
92172
test_spawn_future_interface<false>(sender{}, token<true>{}, *new non_env{});
93173

94174
test_state_base();
175+
test_receiver();
95176
}

0 commit comments

Comments
 (0)