From Jason Turner

[exec.when.all]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmp23voj1tb/{from.md → to.md} +313 -0
tmp/tmp23voj1tb/{from.md → to.md} RENAMED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #### `execution::when_all` <a id="exec.when.all">[[exec.when.all]]</a>
2
+
3
+ `when_all` and `when_all_with_variant` both adapt multiple input senders
4
+ into a sender that completes when all input senders have completed.
5
+ `when_all` only accepts senders with a single value completion signature
6
+ and on success concatenates all the input senders’ value result datums
7
+ into its own value completion operation.
8
+ `when_all_with_variant(sndrs...)` is semantically equivalent to
9
+ w`hen_all(into_variant(sndrs)...)`, where `sndrs` is a pack of
10
+ subexpressions whose types model `sender`.
11
+
12
+ The names `when_all` and `when_all_with_variant` denote customization
13
+ point objects. Let `sndrs` be a pack of subexpressions, let `Sndrs` be a
14
+ pack of the types `decltype((sndrs))...`, and let `CD` be the type
15
+ `common_type_t<decltype(get-domain-early(sndrs))...>`. Let `CD2` be `CD`
16
+ if `CD` is well-formed, and `default_domain` otherwise. The expressions
17
+ `when_all(sndrs...)` and `when_all_with_variant(sndrs...)` are
18
+ ill-formed if any of the following is `true`:
19
+
20
+ - `sizeof...(sndrs)` is `0`, or
21
+ - `(sender<Sndrs> && ...)` is `false`.
22
+
23
+ The expression `when_all(sndrs...)` is expression-equivalent to:
24
+
25
+ ``` cpp
26
+ transform_sender(CD2(), make-sender(when_all, {}, sndrs...))
27
+ ```
28
+
29
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
30
+ specialized for `when_all_t` as follows:
31
+
32
+ ``` cpp
33
+ namespace std::execution {
34
+ template<>
35
+ struct impls-for<when_all_t> : default-impls {
36
+ static constexpr auto get-attrs = see below;
37
+ static constexpr auto get-env = see below;
38
+ static constexpr auto get-state = see below;
39
+ static constexpr auto start = see below;
40
+ static constexpr auto complete = see below;
41
+
42
+ template<class Sndr, class... Env>
43
+ static consteval void check-types();
44
+ };
45
+ }
46
+ ```
47
+
48
+ Let *`make-when-all-env`* be the following exposition-only function
49
+ template:
50
+
51
+ ``` cpp
52
+ template<class Env>
53
+ constexpr auto make-when-all-env(inplace_stop_source& stop_src, // exposition only
54
+ Env&& env) noexcept {
55
+ return see below;
56
+ }
57
+ ```
58
+
59
+ Returns an object `e` such that
60
+
61
+ - `decltype(e)` models `queryable`, and
62
+ - `e.query(get_stop_token)` is expression-equivalent to
63
+ `state.stop-src.get_token()`, and
64
+ - given a query object `q` with type other than cv `get_stop_token_t`
65
+ and whose type satisfies *`forwarding-query`*, `e.query(q)` is
66
+ expression-equivalent to `get_env(rcvr).query(q)`.
67
+
68
+ Let `when-all-env` be an alias template such that `when-all-env<Env>`
69
+ denotes the type
70
+ `decltype(make-{when-all-env}(declval<inplace_stop_source&>(), declval<Env>()))`.
71
+
72
+ ``` cpp
73
+ template<class Sndr, class... Env>
74
+ static consteval void check-types();
75
+ ```
76
+
77
+ Let `Is` be the pack of integral template arguments of the
78
+ `integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
79
+
80
+ *Effects:* Equivalent to:
81
+
82
+ ``` cpp
83
+ auto fn = []<class Child>() {
84
+ auto cs = get_completion_signatures<Child, when-all-env<Env>...>();
85
+ if constexpr (cs.count-of(set_value) >= 2)
86
+ throw unspecified-exception();
87
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
88
+ };
89
+ (fn.template operator()<child-type<Sndr, Is>>(), ...);
90
+ ```
91
+
92
+ where *`unspecified-exception`* is a type derived from `exception`.
93
+
94
+ *Throws:* Any exception thrown as a result of evaluating the *Effects*,
95
+ or an exception of an unspecified type derived from `exception` when
96
+ `CD` is ill-formed.
97
+
98
+ The member `impls-for<when_all_t>::get-attrs` is initialized with a
99
+ callable object equivalent to the following lambda expression:
100
+
101
+ ``` cpp
102
+ [](auto&&, auto&&... child) noexcept {
103
+ if constexpr (same_as<CD, default_domain>) {
104
+ return env<>();
105
+ } else {
106
+ return MAKE-ENV(get_domain, CD());
107
+ }
108
+ }
109
+ ```
110
+
111
+ The member `impls-for<when_all_t>::get-env` is initialized with a
112
+ callable object equivalent to the following lambda expression:
113
+
114
+ ``` cpp
115
+ []<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {
116
+ return make-when-all-env(state.stop-src, get_env(rcvr));
117
+ }
118
+ ```
119
+
120
+ The member `impls-for<when_all_t>::get-state` is initialized with a
121
+ callable object equivalent to the following lambda expression:
122
+
123
+ ``` cpp
124
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
125
+ return e;
126
+ }
127
+ ```
128
+
129
+ where e is the expression
130
+
131
+ ``` cpp
132
+ std::forward<Sndr>(sndr).apply(make-state<Rcvr>())
133
+ ```
134
+
135
+ and where *`make-state`* is the following exposition-only class
136
+ template:
137
+
138
+ ``` cpp
139
+ enum class disposition { started, error, stopped }; // exposition only
140
+
141
+ template<class Rcvr>
142
+ struct make-state {
143
+ template<class... Sndrs>
144
+ auto operator()(auto, auto, Sndrs&&... sndrs) const {
145
+ using values_tuple = see below;
146
+ using errors_variant = see below;
147
+ using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
148
+
149
+ struct state-type {
150
+ void arrive(Rcvr& rcvr) noexcept { // exposition only
151
+ if (0 == --count) {
152
+ complete(rcvr);
153
+ }
154
+ }
155
+
156
+ void complete(Rcvr& rcvr) noexcept; // exposition only
157
+
158
+ atomic<size_t> count{sizeof...(sndrs)}; // exposition only
159
+ inplace_stop_source stop_src{}; // exposition only
160
+ atomic<disposition> disp{disposition::started}; // exposition only
161
+ errors_variant errors{}; // exposition only
162
+ values_tuple values{}; // exposition only
163
+ optional<stop_callback> on_stop{nullopt}; // exposition only
164
+ };
165
+
166
+ return state-type{};
167
+ }
168
+ };
169
+ ```
170
+
171
+ Let *`copy-fail`* be `exception_ptr` if decay-copying any of the child
172
+ senders’ result datums can potentially throw; otherwise, `none-such`,
173
+ where `none-such` is an unspecified empty class type.
174
+
175
+ The alias `values_tuple` denotes the type
176
+
177
+ ``` cpp
178
+ tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>...>
179
+ ```
180
+
181
+ if that type is well-formed; otherwise, `tuple<>`.
182
+
183
+ The alias `errors_variant` denotes the type
184
+ `variant<none-such, copy-fail, Es...>` with duplicate types removed,
185
+ where `Es` is the pack of the decayed types of all the child senders’
186
+ possible error result datums.
187
+
188
+ The member `void state-type::complete(Rcvr& rcvr) noexcept` behaves as
189
+ follows:
190
+
191
+ - If `disp` is equal to `disposition::started`, evaluates:
192
+ ``` cpp
193
+ auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };
194
+ auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
195
+
196
+ on_stop.reset();
197
+ apply(
198
+ [&](auto&... opts) noexcept {
199
+ apply(set, tuple_cat(tie(*opts)...));
200
+ },
201
+ values);
202
+ ```
203
+ - Otherwise, if `disp` is equal to `disposition::error`, evaluates:
204
+ ``` cpp
205
+ on_stop.reset();
206
+ visit(
207
+ [&]<class Error>(Error& error) noexcept {
208
+ if constexpr (!same_as<Error, none-such>) {
209
+ set_error(std::move(rcvr), std::move(error));
210
+ }
211
+ },
212
+ errors);
213
+ ```
214
+ - Otherwise, evaluates:
215
+ ``` cpp
216
+ on_stop.reset();
217
+ set_stopped(std::move(rcvr));
218
+ ```
219
+
220
+ The member `impls-for<when_all_t>::start` is initialized with a callable
221
+ object equivalent to the following lambda expression:
222
+
223
+ ``` cpp
224
+ []<class State, class Rcvr, class... Ops>(
225
+ State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void {
226
+ state.on_stop.emplace(
227
+ get_stop_token(get_env(rcvr)),
228
+ on-stop-request{state.stop_src});
229
+ if (state.stop_src.stop_requested()) {
230
+ state.on_stop.reset();
231
+ set_stopped(std::move(rcvr));
232
+ } else {
233
+ (start(ops), ...);
234
+ }
235
+ }
236
+ ```
237
+
238
+ The member `impls-for<when_all_t>::complete` is initialized with a
239
+ callable object equivalent to the following lambda expression:
240
+
241
+ ``` cpp
242
+ []<class Index, class State, class Rcvr, class Set, class... Args>(
243
+ this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {
244
+ if constexpr (same_as<Set, set_error_t>) {
245
+ if (disposition::error != state.disp.exchange(disposition::error)) {
246
+ state.stop_src.request_stop();
247
+ TRY-EMPLACE-ERROR(state.errors, std::forward<Args>(args)...);
248
+ }
249
+ } else if constexpr (same_as<Set, set_stopped_t>) {
250
+ auto expected = disposition::started;
251
+ if (state.disp.compare_exchange_strong(expected, disposition::stopped)) {
252
+ state.stop_src.request_stop();
253
+ }
254
+ } else if constexpr (!same_as<decltype(State::values), tuple<>>) {
255
+ if (state.disp == disposition::started) {
256
+ auto& opt = get<Index::value>(state.values);
257
+ TRY-EMPLACE-VALUE(complete, opt, std::forward<Args>(args)...);
258
+ }
259
+ }
260
+ state.arrive(rcvr);
261
+ }
262
+ ```
263
+
264
+ where `TRY-EMPLACE-ERROR(v, e)`, for subexpressions `v` and `e`, is
265
+ equivalent to:
266
+
267
+ ``` cpp
268
+ try {
269
+ v.template emplace<decltype(auto(e))>(e);
270
+ } catch (...) {
271
+ v.template emplace<exception_ptr>(current_exception());
272
+ }
273
+ ```
274
+
275
+ if the expression `decltype(auto(e))(e)` is potentially throwing;
276
+ otherwise, `v.template emplace<decltype(auto(e))>(e)`; and where
277
+ `TRY-EMPLACE-VALUE(c, o, as...)`, for subexpressions `c`, `o`, and pack
278
+ of subexpressions `as`, is equivalent to:
279
+
280
+ ``` cpp
281
+ try {
282
+ o.emplace(as...);
283
+ } catch (...) {
284
+ c(Index(), state, rcvr, set_error, current_exception());
285
+ return;
286
+ }
287
+ ```
288
+
289
+ if the expression `decayed-tuple<decltype(as)...>{as...}` is potentially
290
+ throwing; otherwise, `o.emplace(as...)`.
291
+
292
+ The expression `when_all_with_variant(sndrs...)` is
293
+ expression-equivalent to:
294
+
295
+ ``` cpp
296
+ transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));
297
+ ```
298
+
299
+ Given subexpressions `sndr` and `env`, if
300
+ `sender-for<decltype((sndr)), when_all_with_variant_t>` is `false`, then
301
+ the expression `when_all_with_variant.transform_sender(sndr, env)` is
302
+ ill-formed; otherwise, it is equivalent to:
303
+
304
+ ``` cpp
305
+ auto&& [_, _, ...child] = sndr;
306
+ return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
307
+ ```
308
+
309
+ [*Note 1*: This causes the `when_all_with_variant(sndrs...)` sender to
310
+ become `when_all(into_variant(sndrs)...)` when it is connected with a
311
+ receiver whose execution domain does not customize
312
+ `when_all_with_variant`. — *end note*]
313
+