From Jason Turner

[exec.spawn.future]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmp2n24omov/{from.md → to.md} +278 -0
tmp/tmp2n24omov/{from.md → to.md} RENAMED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #### `execution::spawn_future` <a id="exec.spawn.future">[[exec.spawn.future]]</a>
2
+
3
+ `spawn_future` attempts to associate the given input sender with the
4
+ given token’s async scope and, on success, eagerly starts the input
5
+ sender; the return value is a sender that, when connected and started,
6
+ completes with either the result of the eagerly-started input sender or
7
+ with `set_stopped` if the input sender was not started.
8
+
9
+ The name `spawn_future` denotes a customization point object. For
10
+ subexpressions `sndr`, `token`, and `env`,
11
+
12
+ - let `Sndr` be `decltype((sndr))`,
13
+ - let `Token` be `remove_cvref_t<decltype((token))>`, and
14
+ - let `Env` be `remove_cvref_t<decltype((env))>`.
15
+
16
+ If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
17
+ not satisfied, the expression `spawn_future(sndr, token, env)` is
18
+ ill-formed.
19
+
20
+ Let *`spawn-future-state-base`* be the exposition-only class template:
21
+
22
+ ``` cpp
23
+ namespace std::execution {
24
+ template<class Completions>
25
+ struct spawn-future-state-base; // exposition only
26
+
27
+ template<class... Sigs>
28
+ struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition only
29
+ using variant-t = see below; // exposition only
30
+ variant-t result; // exposition only
31
+ virtual void complete() noexcept = 0; // exposition only
32
+ };
33
+ }
34
+ ```
35
+
36
+ Let `Sigs` be the pack of arguments to the `completion_signatures`
37
+ specialization provided as a parameter to the
38
+ *`spawn-future-state-base`* class template. Let *`as-tuple`* be an alias
39
+ template that transforms a completion signature `Tag(Args...)` into the
40
+ tuple specialization `decayed-tuple<Tag, Args...>`.
41
+
42
+ - If `is_nothrow_constructible_v<decay_t<Arg>, Arg>` is `true` for every
43
+ type `Arg` in every parameter pack `Args` in every completion
44
+ signature `Tag(Args...)` in `Sigs` then *`variant-t`* denotes the type
45
+ `variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>`, except
46
+ with duplicate types removed.
47
+ - Otherwise *`variant-t`* denotes the type
48
+ `variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>`,
49
+ except with duplicate types removed.
50
+
51
+ Let *`spawn-future-receiver`* be the exposition-only class template:
52
+
53
+ ``` cpp
54
+ namespace std::execution {
55
+ template<class Completions>
56
+ struct spawn-future-receiver { // exposition only
57
+ using receiver_concept = receiver_t;
58
+
59
+ spawn-future-state-base<Completions>* state; // exposition only
60
+
61
+ template<class... T>
62
+ void set_value(T&&... t) && noexcept {
63
+ set-complete<set_value_t>(std::forward<T>(t)...);
64
+ }
65
+
66
+ template<class E>
67
+ void set_error(E&& e) && noexcept {
68
+ set-complete<set_error_t>(std::forward<E>(e));
69
+ }
70
+
71
+ void set_stopped() && noexcept {
72
+ set-complete<set_stopped_t>();
73
+ }
74
+
75
+ private:
76
+ template<class CPO, class... T>
77
+ void set-complete(T&&... t) noexcept { // exposition only
78
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...);
79
+ try {
80
+ state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{},
81
+ std::forward<T>(t)...);
82
+ }
83
+ catch (...) {
84
+ if constexpr (!nothrow) {
85
+ using tuple_t = decayed-tuple<set_error_t, exception_ptr>;
86
+ state->result.template emplace<tuple_t>(set_error_t{}, current_exception());
87
+ }
88
+ }
89
+ state->complete();
90
+ }
91
+ };
92
+ }
93
+ ```
94
+
95
+ Let `ssource-t` be an unspecified type that models `stoppable-source`
96
+ and let `ssource` be an lvalue of type `ssource-t`. Let `stoken-t` be
97
+ `decltype(ssource.get_token())`. Let *`future-spawned-sender`* be the
98
+ alias template:
99
+
100
+ ``` cpp
101
+ template<sender Sender, class Env>
102
+ using future-spawned-sender = // exposition only
103
+ decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
104
+ ```
105
+
106
+ Let *`spawn-future-state`* be the exposition-only class template:
107
+
108
+ ``` cpp
109
+ namespace std::execution {
110
+ template<class Alloc, scope_token Token, sender Sender, class Env>
111
+ struct spawn-future-state // exposition only
112
+ : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
113
+ using sigs-t = // exposition only
114
+ completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
115
+ using receiver-t = // exposition only
116
+ spawn-future-receiver<sigs-t>;
117
+ using op-t = // exposition only
118
+ connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
119
+
120
+ spawn-future-state(Alloc alloc, Sender&& sndr, Token token, Env env) // exposition only
121
+ : alloc(std::move(alloc)),
122
+ op(connect(
123
+ write_env(stop-when(std::forward<Sender>(sndr), ssource.get_token()), std::move(env)),
124
+ receiver-t(this))),
125
+ token(std::move(token)),
126
+ associated(token.try_associate()) {
127
+ if (associated)
128
+ start(op);
129
+ else
130
+ set_stopped(receiver-t(this));
131
+ }
132
+
133
+ void complete() noexcept override; // exposition only
134
+ void consume(receiver auto& rcvr) noexcept; // exposition only
135
+ void abandon() noexcept; // exposition only
136
+
137
+ private:
138
+ using alloc-t = // exposition only
139
+ allocator_traits<Alloc>::template rebind_alloc<spawn-future-state>;
140
+
141
+ alloc-t alloc; // exposition only
142
+ ssource-t ssource; // exposition only
143
+ op-t op; // exposition only
144
+ Token token; // exposition only
145
+ bool associated; // exposition only
146
+
147
+ void destroy() noexcept; // exposition only
148
+ };
149
+ }
150
+ ```
151
+
152
+ For purposes of determining the existence of a data race, *`complete`*,
153
+ *`consume`*, and *`abandon`* behave as atomic operations
154
+ [[intro.multithread]]. These operations on a single object of a type
155
+ that is a specialization of *`spawn-future-state`* appear to occur in a
156
+ single total order.
157
+
158
+ ``` cpp
159
+ void complete() noexcept;
160
+ ```
161
+
162
+ *Effects:*
163
+
164
+ - No effects if this invocation of *complete* happens before an
165
+ invocation of *consume* or *abandon* on `*this`;
166
+ - otherwise, if an invocation of *consume* on `*this` happens before
167
+ this invocation of *complete* then there is a receiver, `rcvr`,
168
+ registered and that receiver is completed as if by
169
+ *`consume`*`(rcvr)`;
170
+ - otherwise, *destroy* is invoked.
171
+
172
+ ``` cpp
173
+ void consume(receiver auto& rcvr) noexcept;
174
+ ```
175
+
176
+ *Effects:*
177
+
178
+ - If this invocation of *consume* happens before an invocation of
179
+ *complete* on `*this` then `rcvr` is registered to be completed when
180
+ *complete* is subsequently invoked on `*this`;
181
+ - otherwise, `rcvr` is completed as if by:
182
+ ``` cpp
183
+ std::move(this->result).visit(
184
+ [&rcvr](auto&& tuple) noexcept {
185
+ if constexpr (!same_as<remove_reference_t<decltype(tuple)>, monostate>) {
186
+ apply([&rcvr](auto cpo, auto&&... vals) {
187
+ cpo(std::move(rcvr), std::move(vals)...);
188
+ }, std::move(tuple));
189
+ }
190
+ });
191
+ ```
192
+
193
+ ``` cpp
194
+ void abandon() noexcept;
195
+ ```
196
+
197
+ *Effects:*
198
+
199
+ - If this invocation of *abandon* happens before an invocation of
200
+ *complete* on `*this` then equivalent to:
201
+ ``` cpp
202
+ ssource.request_stop();
203
+ ```
204
+ - otherwise, *destroy* is invoked.
205
+
206
+ ``` cpp
207
+ void destroy() noexcept;
208
+ ```
209
+
210
+ *Effects:* Equivalent to:
211
+
212
+ ``` cpp
213
+ auto token = std::move(this->token);
214
+ bool associated = this->associated;
215
+
216
+ {
217
+ auto alloc = std::move(this->alloc);
218
+
219
+ allocator_traits<alloc-t>::destroy(alloc, this);
220
+ allocator_traits<alloc-t>::deallocate(alloc, this, 1);
221
+ }
222
+
223
+ if (associated)
224
+ token.disassociate();
225
+ ```
226
+
227
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
228
+ specialized for `spawn_future_t` as follows:
229
+
230
+ ``` cpp
231
+ namespace std::execution {
232
+ template<>
233
+ struct impls-for<spawn_future_t> : default-impls {
234
+ static constexpr auto start = see below; // exposition only
235
+ };
236
+ }
237
+ ```
238
+
239
+ The member `impls-for<spawn_future_t>::start` is initialized with a
240
+ callable object equivalent to the following lambda:
241
+
242
+ ``` cpp
243
+ [](auto& state, auto& rcvr) noexcept -> void {
244
+ state->consume(rcvr);
245
+ }
246
+ ```
247
+
248
+ For the expression `spawn_future(sndr, token, env)` let `new_sender` be
249
+ the expression `token.wrap(sndr)` and let `alloc` and `senv` be defined
250
+ as follows:
251
+
252
+ - if the expression `get_allocator(env)` is well-formed, then `alloc` is
253
+ the result of `get_allocator(env)` and `senv` is the expression `env`;
254
+ - otherwise, if the expression `get_allocator(get_env(new_sender))` is
255
+ well-formed, then `alloc` is the result of
256
+ `get_allocator(get_env(new_sender))` and `senv` is the expression
257
+ `JOIN-ENV(prop(get_allocator, alloc), env)`;
258
+ - otherwise, `alloc` is `allocator<void>()` and `senv` is the expression
259
+ `env`.
260
+
261
+ The expression `spawn_future(sndr, token, env)` has the following
262
+ effects:
263
+
264
+ - Uses `alloc` to allocate and construct an object `s` of a type that is
265
+ a specialization of *`spawn-future-{}state`* from `alloc`,
266
+ `token.wrap(sndr)`, `token`, and `senv`. If an exception is thrown
267
+ then any constructed objects are destroyed and any allocated memory is
268
+ deallocated.
269
+ - Constructs an object `u` of a type that is a specialization of
270
+ `unique_ptr` such that:
271
+ - `u.get()` is equal to the address of `s`, and
272
+ - `u.get_deleter()(u.release())` is equivalent to
273
+ `u.release()->abandon()`.
274
+ - Returns `make-sender(spawn_future, std::move(u))`.
275
+
276
+ The expression `spawn_future(sndr, token)` is expression-equivalent to
277
+ `spawn_future(sndr, token, execution::env<>())`.
278
+