Skip to content

Commit 3d2595e

Browse files
committed
some progress on implementing spawn_future
1 parent 58bd68c commit 3d2595e

File tree

6 files changed

+201
-12
lines changed

6 files changed

+201
-12
lines changed

include/beman/execution/detail/prop.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
namespace beman::execution {
1212
template <typename Query, typename Value>
1313
struct prop {
14-
[[no_unique_address]] Query query_;
14+
[[no_unique_address]] Query query_{};
1515
Value value_;
1616

17+
template <typename Q, typename V>
18+
prop(Q q, V&& v) : query_(q), value_(v) {}
19+
prop(prop&&) = default;
20+
prop(const prop&) = default;
21+
auto operator=(prop&&) = delete;
1722
auto operator=(const prop&) = delete;
1823

1924
constexpr auto query(Query) const noexcept -> Value { return this->value_; }

include/beman/execution/detail/simple_counting_scope.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,9 @@ class beman::execution::simple_counting_scope : ::beman::execution::detail::immo
165165
::std::exchange(current, current->next)->complete();
166166
}
167167
}
168-
::std::mutex mutex;
169-
::std::size_t count{};
170-
state_t state{state_t::unused};
168+
::std::mutex mutex;
169+
::std::size_t count{};
170+
state_t state{state_t::unused};
171171
::beman::execution::detail::simple_counting_scope_state_base* head{};
172172
};
173173

include/beman/execution/detail/spawn_future.hpp

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,25 @@
66

77
#include <beman/execution/detail/sender.hpp>
88
#include <beman/execution/detail/async_scope_token.hpp>
9+
#include <beman/execution/detail/get_allocator.hpp>
10+
#include <beman/execution/detail/get_env.hpp>
11+
#include <beman/execution/detail/make_sender.hpp>
12+
#include <beman/execution/detail/completion_signatures_of_t.hpp>
913
#include <beman/execution/detail/empty_env.hpp>
1014
#include <beman/execution/detail/queryable.hpp>
1115
#include <beman/execution/detail/as_tuple.hpp>
1216
#include <beman/execution/detail/meta_unique.hpp>
1317
#include <beman/execution/detail/receiver.hpp>
18+
#include <beman/execution/detail/prop.hpp>
19+
#include <beman/execution/detail/join_env.hpp>
1420
#include <beman/execution/detail/set_error.hpp>
1521
#include <beman/execution/detail/set_stopped.hpp>
1622
#include <beman/execution/detail/set_value.hpp>
23+
#include <beman/execution/detail/stop_when.hpp>
24+
#include <beman/execution/detail/write_env.hpp>
25+
#include <beman/execution/detail/inplace_stop_source.hpp>
26+
#include <mutex>
27+
#include <memory>
1728
#include <utility>
1829
#include <variant>
1930
#include <type_traits>
@@ -82,11 +93,91 @@ struct spawn_future_receiver {
8293
}
8394
};
8495

96+
template <::beman::execution::sender Sndr, typename Env>
97+
using future_spawned_sender = decltype(::beman::execution::write_env(
98+
::beman::execution::detail::stop_when(::std::declval<Sndr>(),
99+
::std::declval<::beman::execution::inplace_stop_token>()),
100+
::std::declval<Env>()));
101+
102+
template <typename Allocator,
103+
::beman::execution::async_scope_token Token,
104+
::beman::execution::sender Sndr,
105+
typename Env>
106+
struct spawn_future_state
107+
: ::beman::execution::detail::spawn_future_state_base<::beman::execution::completion_signatures_of_t<
108+
::beman::execution::detail::future_spawned_sender<Sndr, Env>>> {
109+
using alloc_t = typename ::std::allocator_traits<Allocator>::template rebind_alloc<spawn_future_state>;
110+
using traits_t = ::std::allocator_traits<alloc_t>;
111+
112+
spawn_future_state(auto a, auto&&, Token tok, auto&&...) : token(tok), alloc(a) { /*-dk:TODO*/ }
113+
auto complete() noexcept -> void override { /*-dk:TODO*/ }
114+
auto abandon() noexcept -> void {
115+
/*-dk:TODO*/
116+
this->destroy();
117+
}
118+
auto destroy() noexcept -> void {
119+
Token tok{this->token};
120+
bool assoc{this->associated};
121+
{
122+
alloc_t a{this->alloc};
123+
traits_t::destroy(a, this);
124+
traits_t::deallocate(a, this, 1u);
125+
}
126+
if (assoc) {
127+
tok.disassociate();
128+
}
129+
}
130+
131+
::std::mutex cerberos{};
132+
Token token;
133+
bool associated{false};
134+
alloc_t alloc;
135+
};
136+
137+
template <::beman::execution::sender Sndr, typename Ev>
138+
auto spawn_future_get_allocator(const Sndr& sndr, const Ev& ev) {
139+
if constexpr (requires { ::beman::execution::get_allocator(ev); }) {
140+
return ::std::pair(::beman::execution::get_allocator(ev), ev);
141+
} else if constexpr (requires { ::beman::execution::get_allocator(::beman::execution::get_env(sndr)); }) {
142+
auto alloc{::beman::execution::get_allocator(::beman::execution::get_env(sndr))};
143+
return ::std::pair(alloc,
144+
::beman::execution::detail::join_env(
145+
::beman::execution::prop(::beman::execution::get_allocator, alloc), ev));
146+
} else {
147+
return ::std::pair(::std::allocator<void>{}, ev);
148+
}
149+
}
150+
85151
class spawn_future_t {
86152
public:
87-
template <::beman::execution::sender Sndr, ::beman::execution::async_scope_token Tok, typename Env>
88-
requires ::beman::execution::detail::queryable<::std::remove_cvref_t<Env>>
89-
auto operator()(Sndr&&, Tok&&, Env&&) const {}
153+
template <::beman::execution::sender Sndr, ::beman::execution::async_scope_token Tok, typename Ev>
154+
requires ::beman::execution::detail::queryable<::std::remove_cvref_t<Ev>>
155+
auto operator()(Sndr&& sndr, Tok&& tok, Ev&& ev) const {
156+
auto make{[&] -> decltype(auto) { //-dk:TODO while decltype(auto) instead of auto?
157+
return tok.wrap(::std::forward<Sndr>(sndr));
158+
}};
159+
using sndr_t = decltype(make());
160+
static_assert(::beman::execution::sender<Sndr>);
161+
162+
auto [alloc, senv] = spawn_future_get_allocator(sndr, ev);
163+
using state_t = ::beman::execution::detail::spawn_future_state<decltype(alloc), Tok, sndr_t, decltype(senv)>;
164+
using state_alloc_t = typename ::std::allocator_traits<decltype(alloc)>::template rebind_alloc<state_t>;
165+
using state_traits_t = ::std::allocator_traits<state_alloc_t>;
166+
state_alloc_t state_alloc(alloc);
167+
using deleter = decltype([](state_t* p) noexcept {
168+
if (p)
169+
p->abandon();
170+
});
171+
state_t* op{state_traits_t::allocate(state_alloc, 1u)};
172+
try {
173+
state_traits_t::construct(state_alloc, op, alloc, make(), tok, senv);
174+
} catch (...) {
175+
state_traits_t::deallocate(state_alloc, op, 1u);
176+
throw;
177+
}
178+
179+
return ::beman::execution::detail::make_sender(*this, ::std::unique_ptr<state_t, deleter>{op});
180+
}
90181
template <::beman::execution::sender Sndr, ::beman::execution::async_scope_token Tok>
91182
auto operator()(Sndr&& sndr, Tok&& tok) const {
92183
return (*this)(::std::forward<Sndr>(sndr), ::std::forward<Tok>(tok), ::beman::execution::empty_env{});

include/beman/execution/detail/write_env.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ struct impls_for<write_env_t> : ::beman::execution::detail::default_impls {
4545
inline constexpr write_env_t write_env{};
4646
} // namespace beman::execution::detail
4747

48+
namespace beman::execution {
49+
using write_env_t = ::beman::execution::detail::write_env_t;
50+
inline constexpr write_env_t write_env{};
51+
} // namespace beman::execution
52+
4853
// ----------------------------------------------------------------------------
4954

5055
#endif

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

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
#include <beman/execution/detail/sender.hpp>
77
#include <beman/execution/detail/async_scope_token.hpp>
88
#include <beman/execution/detail/receiver.hpp>
9+
#include <beman/execution/detail/simple_allocator.hpp>
10+
#include <beman/execution/detail/get_allocator.hpp>
11+
#include <beman/execution/detail/get_stop_token.hpp>
12+
#include <beman/execution/detail/join_env.hpp>
13+
#include <beman/execution/detail/inplace_stop_source.hpp>
914
#include <test/execution.hpp>
1015
#include <concepts>
1116

@@ -17,14 +22,18 @@ struct non_env {
1722
};
1823
static_assert(not test_detail::queryable<non_env>);
1924

20-
struct env {};
25+
struct env {
26+
int value{};
27+
auto operator==(const env&) const -> bool = default;
28+
};
2129
static_assert(test_detail::queryable<env>);
2230

2331
struct non_sender {};
2432
static_assert(not test_std::sender<non_sender>);
2533

2634
struct sender {
27-
using sender_concept = test_std::sender_t;
35+
using sender_concept = test_std::sender_t;
36+
using completion_signatures = test_std::completion_signatures<test_std::set_value_t()>;
2837
};
2938
static_assert(test_std::sender<sender>);
3039

@@ -55,7 +64,7 @@ auto test_spawn_future_interface(Sender&& sndr, Token&& tok) -> void {
5564
static_assert(Expect ==
5665
requires { test_std::spawn_future(std::forward<Sender>(sndr), std::forward<Token>(tok)); });
5766
}
58-
static_assert(Expect == requires(Env const& e){
67+
static_assert(Expect == requires(const Env& e) {
5968
test_std::spawn_future(std::forward<Sender>(sndr), std::forward<Token>(tok), e);
6069
});
6170
}
@@ -182,6 +191,81 @@ auto test_receiver() {
182191
}
183192
}
184193
}
194+
195+
template <test_std::sender Sndr, test_std::async_scope_token Tok, typename Ev>
196+
auto test_spawn_future(Sndr&& sndr, Tok&& tok, Ev&& ev) {
197+
test_std::spawn_future(std::forward<Sndr>(sndr), std::forward<Tok>(tok), std::forward<Ev>(ev));
198+
}
199+
200+
struct allocator {
201+
using value_type = int;
202+
int value{};
203+
204+
auto allocate(std::size_t n) -> int* { return new int[n]; }
205+
auto deallocate(int* p, std::size_t) -> void { delete[] p; }
206+
207+
auto operator==(const allocator&) const -> bool = default;
208+
};
209+
static_assert(test_detail::simple_allocator<allocator>);
210+
211+
struct alloc_env {
212+
int value{};
213+
auto operator==(const alloc_env&) const -> bool = default;
214+
215+
auto query(const test_std::get_allocator_t&) const noexcept -> allocator { return allocator{this->value}; }
216+
};
217+
218+
struct alloc_sender {
219+
using sender_concept = test_std::sender_t;
220+
int value{};
221+
222+
auto get_env() const noexcept -> alloc_env { return alloc_env{this->value}; }
223+
};
224+
static_assert(test_std::sender<alloc_sender>);
225+
226+
auto test_get_allocator() {
227+
{
228+
alloc_env ae{87};
229+
auto [alloc, ev] = test_detail::spawn_future_get_allocator(sender{}, ae);
230+
static_assert(std::same_as<decltype(alloc), allocator>);
231+
ASSERT(alloc == allocator{87});
232+
static_assert(std::same_as<decltype(ev), alloc_env>);
233+
ASSERT(ev == alloc_env{87});
234+
}
235+
{
236+
auto [alloc, ev] = test_detail::spawn_future_get_allocator(alloc_sender{53}, env{42});
237+
static_assert(std::same_as<decltype(alloc), allocator>);
238+
ASSERT(alloc == allocator{53});
239+
ASSERT(test_std::get_allocator(ev) == allocator{53});
240+
}
241+
{
242+
test_std::inplace_stop_source source;
243+
auto [alloc, ev] = test_detail::spawn_future_get_allocator(
244+
alloc_sender{53}, test_std::prop(test_std::get_stop_token, source.get_token()));
245+
static_assert(std::same_as<decltype(alloc), allocator>);
246+
ASSERT(alloc == allocator{53});
247+
ASSERT(test_std::get_allocator(ev) == allocator{53});
248+
ASSERT(test_std::get_stop_token(ev) == source.get_token());
249+
}
250+
{
251+
test_std::inplace_stop_source source;
252+
auto [alloc, ev] = test_detail::spawn_future_get_allocator(
253+
alloc_sender{53},
254+
test_detail::join_env(test_std::prop(test_std::get_allocator, allocator(101)),
255+
test_std::prop(test_std::get_stop_token, source.get_token())));
256+
static_assert(std::same_as<decltype(alloc), allocator>);
257+
ASSERT(alloc == allocator{101});
258+
ASSERT(test_std::get_allocator(ev) == allocator{101});
259+
ASSERT(test_std::get_stop_token(ev) == source.get_token());
260+
}
261+
{
262+
auto [alloc, ev] = test_detail::spawn_future_get_allocator(sender{}, env{42});
263+
static_assert(std::same_as<decltype(alloc), std::allocator<void>>);
264+
static_assert(std::same_as<decltype(ev), env>);
265+
ASSERT(ev == env{42});
266+
}
267+
}
268+
185269
} // namespace
186270

187271
TEST(exec_spawn_future) {
@@ -194,4 +278,8 @@ TEST(exec_spawn_future) {
194278

195279
test_state_base();
196280
test_receiver();
281+
282+
test_get_allocator();
283+
284+
test_spawn_future(sender{}, token<true>{}, env{});
197285
}

tests/beman/execution/include/test/execution.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ using source_location = ::std::source_location;
3333
#else
3434
struct source_location {
3535
static auto current() -> source_location { return {}; }
36-
auto file_name() const -> char const* { return "<unknown:no std::source_location>"; }
37-
auto line() const -> char const* { return "<unknown:no std::source_location>"; }
36+
auto file_name() const -> const char* { return "<unknown:no std::source_location>"; }
37+
auto line() const -> const char* { return "<unknown:no std::source_location>"; }
3838
};
3939
#endif
4040

0 commit comments

Comments
 (0)