Skip to content

Commit 34c7c35

Browse files
committed
Add stdexec::spawn_future
This diff adds `stdexec::spawn_future` and some basic tests.
1 parent c68fac1 commit 34c7c35

File tree

8 files changed

+790
-136
lines changed

8 files changed

+790
-136
lines changed

include/stdexec/__detail/__spawn.hpp

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "__receivers.hpp"
2525
#include "__scope_concepts.hpp"
2626
#include "__senders_core.hpp"
27+
#include "__spawn_common.hpp"
2728
#include "__type_traits.hpp"
2829
#include "__write_env.hpp"
2930

@@ -98,53 +99,6 @@ namespace stdexec {
9899
__assoc_t __assoc_;
99100
};
100101

101-
struct __choose_alloc_fn {
102-
template <class _Env, class _SenderEnv>
103-
requires __callable<get_allocator_t, _Env>
104-
auto operator()(const _Env& __env, const _SenderEnv&) const {
105-
return get_allocator(__env);
106-
}
107-
108-
template <class _Env, class _SenderEnv>
109-
requires(!__callable<get_allocator_t, const _Env&>)
110-
&& __callable<get_allocator_t, const _SenderEnv&>
111-
auto operator()(const _Env&, const _SenderEnv& __env) const {
112-
return get_allocator(__env);
113-
}
114-
115-
template <class _Env, class _SenderEnv>
116-
requires(!__callable<get_allocator_t, const _Env&>)
117-
&& (!__callable<get_allocator_t, const _SenderEnv&>)
118-
std::allocator<void> operator()(const _Env&, const _SenderEnv&) const {
119-
return std::allocator<void>();
120-
}
121-
};
122-
123-
inline constexpr __choose_alloc_fn __choose_alloc{};
124-
125-
struct __choose_senv_fn {
126-
template <class _Env, class _SenderEnv>
127-
requires __callable<get_allocator_t, const _Env&>
128-
const _Env& operator()(const _Env& __env, const _SenderEnv&) const {
129-
return __env;
130-
}
131-
132-
template <class _Env, class _SenderEnv>
133-
requires(!__callable<get_allocator_t, const _Env&>)
134-
&& __callable<get_allocator_t, const _SenderEnv&>
135-
auto operator()(const _Env& __env, const _SenderEnv& __sndrEnv) const {
136-
return __env::__join(prop(get_allocator, get_allocator(__sndrEnv)), __env);
137-
}
138-
139-
template <class _Env, class _SenderEnv>
140-
requires(!__callable<get_allocator_t, const _Env&>)
141-
&& (!__callable<get_allocator_t, const _SenderEnv&>)
142-
const _Env& operator()(const _Env& __env, const _SenderEnv&) const {
143-
return __env;
144-
}
145-
};
146-
147-
inline constexpr __choose_senv_fn __choose_senv{};
148102

149103
struct spawn_t {
150104
template <sender _Sender, scope_token _Token>
@@ -157,15 +111,16 @@ namespace stdexec {
157111
auto wrappedSender = __tkn.wrap(static_cast<_Sender&&>(__sndr));
158112
auto sndrEnv = get_env(wrappedSender);
159113

160-
using raw_alloc = decltype(__choose_alloc(__env, sndrEnv));
114+
using raw_alloc = decltype(__spawn_common::__choose_alloc(__env, sndrEnv));
161115

162-
auto senderWithEnv = write_env(std::move(wrappedSender), __choose_senv(__env, sndrEnv));
116+
auto senderWithEnv =
117+
write_env(std::move(wrappedSender), __spawn_common::__choose_senv(__env, sndrEnv));
163118

164119
using spawn_state_t =
165120
__spawn_state<raw_alloc, std::remove_cvref_t<_Token>, decltype(senderWithEnv)>;
166121

167122
using traits = std::allocator_traits<raw_alloc>::template rebind_traits<spawn_state_t>;
168-
typename traits::allocator_type alloc(__choose_alloc(__env, sndrEnv));
123+
typename traits::allocator_type alloc(__spawn_common::__choose_alloc(__env, sndrEnv));
169124

170125
auto* op = traits::allocate(alloc, 1);
171126

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2025 Ian Petersen
3+
* Copyright (c) 2025 NVIDIA Corporation
4+
*
5+
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://llvm.org/LICENSE.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#pragma once
18+
19+
#include "__execution_fwd.hpp"
20+
21+
#include "__concepts.hpp"
22+
#include "__env.hpp"
23+
#include "__queries.hpp"
24+
25+
#include <memory>
26+
27+
namespace stdexec {
28+
////////////////////////////////////////////////////////////////////////////////////////////////
29+
// [exec.spawn] paragraph 9
30+
// [exec.spawn.future] paragraph 15
31+
//
32+
// spawn and spawn_future both have to choose an allocator and an injected environment, and they
33+
// do it in the same way; this namespace provides a couple of functions for making those choices
34+
namespace __spawn_common {
35+
struct __choose_alloc_fn {
36+
// [exec.spawn] paragraph 9
37+
// [exec.spawn.future] paragraph 15
38+
// (9/15.1) -- if the expression get_allocator(env) is well-formed, then alloc is
39+
// the result of get_allocator(env)
40+
template <class _Env, class _SenderEnv>
41+
requires __callable<get_allocator_t, _Env>
42+
auto operator()(const _Env& __env, const _SenderEnv&) const {
43+
return get_allocator(__env);
44+
}
45+
46+
// [exec.spawn] paragraph 9
47+
// [exec.spawn.future] paragraph 15
48+
// (9/15.2) -- otherwise, if the expression get_allocator(get_env(new_sender)) is
49+
// well-formed, then alloc is the result of get_allocator(get_env(new_sender))
50+
template <class _Env, class _SenderEnv>
51+
requires(!__callable<get_allocator_t, const _Env&>)
52+
&& __callable<get_allocator_t, const _SenderEnv&>
53+
auto operator()(const _Env&, const _SenderEnv& __env) const {
54+
return get_allocator(__env);
55+
}
56+
57+
// [exec.spawn] paragraph 9
58+
// [exec.spawn.future] paragraph 15
59+
// (9/15.3) -- otherwise, alloc is allocator<void>()
60+
template <class _Env, class _SenderEnv>
61+
requires(!__callable<get_allocator_t, const _Env&>)
62+
&& (!__callable<get_allocator_t, const _SenderEnv&>)
63+
std::allocator<void> operator()(const _Env&, const _SenderEnv&) const {
64+
return std::allocator<void>();
65+
}
66+
};
67+
68+
inline constexpr __choose_alloc_fn __choose_alloc{};
69+
70+
struct __choose_senv_fn {
71+
// [exec.spawn] paragraph 9
72+
// [exec.spawn.future] paragraph 15
73+
// (9/15.1) -- if the expression get_allocator(env) is well-formed, then ...
74+
// senv is the expression env;
75+
template <class _Env, class _SenderEnv>
76+
requires __callable<get_allocator_t, const _Env&>
77+
const _Env& operator()(const _Env& __env, const _SenderEnv&) const {
78+
return __env;
79+
}
80+
81+
// [exec.spawn] paragraph 9
82+
// [exec.spawn.future] paragraph 15
83+
// (9/15.2) -- otherwise, if the expression get_allocator(get_env(new_sender)) is
84+
// well-formed, then ... senv is the expression
85+
// JOIN-ENV(prop(get_allocator, alloc), env);
86+
template <class _Env, class _SenderEnv>
87+
requires(!__callable<get_allocator_t, const _Env&>)
88+
&& __callable<get_allocator_t, const _SenderEnv&>
89+
auto operator()(const _Env& __env, const _SenderEnv& __sndrEnv) const {
90+
return __env::__join(prop(get_allocator, get_allocator(__sndrEnv)), __env);
91+
}
92+
93+
// [exec.spawn] paragraph 9
94+
// [exec.spawn.future] paragraph 15
95+
// (9/15.3) -- otherwise, ... senv is the expression env.
96+
template <class _Env, class _SenderEnv>
97+
requires(!__callable<get_allocator_t, const _Env&>)
98+
&& (!__callable<get_allocator_t, const _SenderEnv&>)
99+
const _Env& operator()(const _Env& __env, const _SenderEnv&) const {
100+
return __env;
101+
}
102+
};
103+
104+
inline constexpr __choose_senv_fn __choose_senv{};
105+
} // namespace __spawn_common
106+
} // namespace stdexec

0 commit comments

Comments
 (0)