From Jason Turner

[exec.scope]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmpkb6n52qh/{from.md → to.md} +402 -0
tmp/tmpkb6n52qh/{from.md → to.md} RENAMED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Execution scope utilities <a id="exec.scope">[[exec.scope]]</a>
2
+
3
+ ### Execution scope concepts <a id="exec.scope.concepts">[[exec.scope.concepts]]</a>
4
+
5
+ The `scope_token` concept defines the requirements on a type `Token`
6
+ that can be used to create associations between senders and an async
7
+ scope.
8
+
9
+ Let *test-sender* and *test-env* be unspecified types such that
10
+ `sender_in<test-sender, test-env>` is modeled.
11
+
12
+ ``` cpp
13
+ namespace std::execution {
14
+ template<class Token>
15
+ concept scope_token =
16
+ copyable<Token> &&
17
+ requires(const Token token) {
18
+ { token.try_associate() } -> same_as<bool>;
19
+ { token.disassociate() } noexcept -> same_as<void>;
20
+ { token.wrap(declval<test-sender>()) } -> sender_in<test-env>;
21
+ };
22
+ }
23
+ ```
24
+
25
+ A type `Token` models `scope_token` only if:
26
+
27
+ - no exceptions are thrown from copy construction, move construction,
28
+ copy assignment, or move assignment of objects of type `Token`; and
29
+ - given an lvalue `token` of type (possibly const) `Token`, for all
30
+ expressions `sndr` such that `decltype(({}sndr))` models `sender`:
31
+ - `token.wrap(sndr)` is a valid expression,
32
+ - `decltype(token.wrap(sndr))` models `sender`, and
33
+ - `completion_signatures_of_t<decltype(token.wrap(sndr)), E>` contains
34
+ the same completion signatures as
35
+ `completion_signatures_of_t<decltype((sndr)), E>` for all types `E`
36
+ such that `sender_in<decltype((sndr)), E>` is modeled.
37
+
38
+ ### Counting Scopes <a id="exec.counting.scopes">[[exec.counting.scopes]]</a>
39
+
40
+ #### General <a id="exec.counting.scopes.general">[[exec.counting.scopes.general]]</a>
41
+
42
+ Scopes of type `simple_counting_scope` and `counting_scope` maintain
43
+ counts of associations. Let:
44
+
45
+ - `Scope` be either `simple_counting_scope` or `counting_scope`,
46
+ - `scope` be an object of type `Scope`,
47
+ - `tkn` be an object of type `Scope::token` obtained from
48
+ `scope.get_token()`,
49
+ - `jsndr` be a sender obtained from `scope.join()`, and
50
+ - `op` be an operation state obtained from connecting `jsndr` to a
51
+ receiver.
52
+
53
+ During its lifetime `scope` goes through different states which govern
54
+ what operations are allowed and the result of these operations:
55
+
56
+ - *`unused`*: a newly constructed object starts in the *`unused`* state.
57
+ - *`open`*: when `tkn.try_associate()` is called while `scope` is in the
58
+ *`unused`* state, `scope` moves to the *`open`* state.
59
+ - *`open-and-joining`*: when the operation state `op` is started while
60
+ `scope` is in the *`unused`* or *`open`* state, `scope` moves to the
61
+ *`open-and-joining`* state.
62
+ - *`closed`*: when `scope.close()` is called while `scope` is in the
63
+ *`open`* state, `scope` moves to the *`closed`* state.
64
+ - *`unused-and-closed`*: when `scope.close()` is called while `scope` is
65
+ in the *`unused`* state, `scope` moves to the *`unused-and-closed`*
66
+ state.
67
+ - *`closed-and-joining`*: when `scope.close()` is called while `scope`
68
+ is in the *`open-and-joining`* state or the operation state `op` is
69
+ started while `scope` is in the *`closed`* or *`unused-and-closed`*
70
+ state, `scope` moves to the *`closed-and-joining`* state.
71
+ - *`joined`*: when the count of associations drops to zero while `scope`
72
+ is in the *`open-and-joining`* or *`closed-and-joining`* state,
73
+ `scope` moves to the *`joined`* state.
74
+
75
+ *Recommended practice:* For `simple_counting_scope` and
76
+ `counting_scope`, implementations should store the state and the count
77
+ of associations in a single member of type `size_t`.
78
+
79
+ Subclause [[exec.counting.scopes]] makes use of the following
80
+ exposition-only entities:
81
+
82
+ ``` cpp
83
+ struct scope-join-t {}; // exposition only
84
+
85
+ enum scope-state-type { // exposition only
86
+ unused, // exposition only
87
+ open, // exposition only
88
+ closed, // exposition only
89
+ open-and-joining, // exposition only
90
+ closed-and-joining, // exposition only
91
+ unused-and-closed, // exposition only
92
+ joined, // exposition only
93
+ };
94
+ ```
95
+
96
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
97
+ specialized for *`scope-join-t`* as follows:
98
+
99
+ ``` cpp
100
+ namespace std::execution {
101
+ template<>
102
+ struct impls-for<scope-join-t> : default-impls {
103
+ template<class Scope, class Rcvr>
104
+ struct state { // exposition only
105
+ struct rcvr-t { // exposition only
106
+ using receiver_concept = receiver_t;
107
+
108
+ Rcvr& rcvr; // exposition only
109
+
110
+ void set_value() && noexcept {
111
+ execution::set_value(std::move(rcvr));
112
+ }
113
+
114
+ template<class E>
115
+ void set_error(E&& e) && noexcept {
116
+ execution::set_error(std::move(rcvr), std::forward<E>(e));
117
+ }
118
+
119
+ void set_stopped() && noexcept {
120
+ execution::set_stopped(std::move(rcvr));
121
+ }
122
+
123
+ decltype(auto) get_env() const noexcept {
124
+ return execution::get_env(rcvr);
125
+ }
126
+ };
127
+
128
+ using sched-sender = // exposition only
129
+ decltype(schedule(get_scheduler(get_env(declval<Rcvr&>()))));
130
+ using op-t = // exposition only
131
+ connect_result_t<sched-sender, rcvr-t>;
132
+
133
+ Scope* scope; // exposition only
134
+ Rcvr& receiver; // exposition only
135
+ op-t op; // exposition only
136
+
137
+ state(Scope* scope, Rcvr& rcvr) // exposition only
138
+ noexcept(nothrow-callable<connect_t, sched-sender, rcvr-t>)
139
+ : scope(scope),
140
+ receiver(rcvr),
141
+ op(connect(schedule(get_scheduler(get_env(rcvr))), rcvr-t(rcvr))) {}
142
+
143
+ void complete() noexcept { // exposition only
144
+ start(op);
145
+ }
146
+
147
+ void complete-inline() noexcept { // exposition only
148
+ set_value(std::move(receiver));
149
+ }
150
+ };
151
+
152
+ static constexpr auto get-state = // exposition only
153
+ []<class Rcvr>(auto&& sender, Rcvr& receiver)
154
+ noexcept(is_nothrow_constructible_v<state<Rcvr>, data-type<decltype(sender)>, Rcvr&>) {
155
+ auto[_, self] = sender;
156
+ return state(self, receiver);
157
+ };
158
+
159
+ static constexpr auto start = // exposition only
160
+ [](auto& s, auto&) noexcept {
161
+ if (s.scope->start-join-sender(s))
162
+ s.complete-inline();
163
+ };
164
+ };
165
+ }
166
+ ```
167
+
168
+ #### Simple Counting Scope <a id="exec.scope.simple.counting">[[exec.scope.simple.counting]]</a>
169
+
170
+ ##### General <a id="exec.scope.simple.counting.general">[[exec.scope.simple.counting.general]]</a>
171
+
172
+ ``` cpp
173
+ namespace std::execution {
174
+ class simple_counting_scope {
175
+ public:
176
+ // [exec.simple.counting.token], token
177
+ struct token;
178
+
179
+ static constexpr size_t max_associations = implementation-defined;
180
+
181
+ // [exec.simple.counting.ctor], constructor and destructor
182
+ simple_counting_scope() noexcept;
183
+ simple_counting_scope(simple_counting_scope&&) = delete;
184
+ ~simple_counting_scope();
185
+
186
+ // [exec.simple.counting.mem], members
187
+ token get_token() noexcept;
188
+ void close() noexcept;
189
+ sender auto join() noexcept;
190
+
191
+ private:
192
+ size_t count; // exposition only
193
+ scope-state-type state; // exposition only
194
+
195
+ bool try-associate() noexcept; // exposition only
196
+ void disassociate() noexcept; // exposition only
197
+ template<class State>
198
+ bool start-join-sender(State& state) noexcept; // exposition only
199
+ };
200
+ }
201
+ ```
202
+
203
+ For purposes of determining the existence of a data race, `get_token`,
204
+ `close`, `join`, *`try-associate`*, *`disassociate`*, and
205
+ *`start-join-sender`* behave as atomic operations [[intro.multithread]].
206
+ These operations on a single object of type `simple_counting_scope`
207
+ appear to occur in a single total order.
208
+
209
+ ##### Constructor and Destructor <a id="exec.simple.counting.ctor">[[exec.simple.counting.ctor]]</a>
210
+
211
+ ``` cpp
212
+ simple_counting_scope() noexcept;
213
+ ```
214
+
215
+ *Ensures:* *count* is `0` and *state* is *unused*.
216
+
217
+ ``` cpp
218
+ ~simple_counting_scope();
219
+ ```
220
+
221
+ *Effects:* If *state* is not one of *joined*, *unused*, or
222
+ *unused-and-closed*, invokes `terminate` [[except.terminate]].
223
+ Otherwise, has no effects.
224
+
225
+ ##### Members <a id="exec.simple.counting.mem">[[exec.simple.counting.mem]]</a>
226
+
227
+ ``` cpp
228
+ token get_token() noexcept;
229
+ ```
230
+
231
+ *Returns:* An object `t` of type `simple_counting_scope::token` such
232
+ that `t.`*`scope`*` == this` is `true`.
233
+
234
+ ``` cpp
235
+ void close() noexcept;
236
+ ```
237
+
238
+ *Effects:* If *state* is
239
+
240
+ - *unused*, then changes *state* to *unused-and-closed*;
241
+ - *open*, then changes *state* to *closed*;
242
+ - *open-and-joining*, then changes *state* to *closed-and-joining*;
243
+ - otherwise, no effects.
244
+
245
+ *Ensures:* Any subsequent call to *`try-associate`*`()` on `*this`
246
+ returns `false`.
247
+
248
+ ``` cpp
249
+ sender auto join() noexcept;
250
+ ```
251
+
252
+ *Returns:* *`make-sender`*`(`*`scope-join-t`*`(), this)`.
253
+
254
+ ``` cpp
255
+ bool try-associate() noexcept;
256
+ ```
257
+
258
+ *Effects:* If *count* is equal to `max_associations`, then no effects.
259
+ Otherwise, if *state* is
260
+
261
+ - *unused*, then increments *count* and changes *state* to *open*;
262
+ - *open* or *open-and-joining*, then increments *count*;
263
+ - otherwise, no effects.
264
+
265
+ *Returns:* `true` if *count* was incremented, `false` otherwise.
266
+
267
+ ``` cpp
268
+ void disassociate() noexcept;
269
+ ```
270
+
271
+ *Preconditions:* *count* is greater than zero.
272
+
273
+ *Effects:* Decrements *count*. If *count* is zero after decrementing and
274
+ *state* is *open-and-joining* or *closed-and-joining*, changes *state*
275
+ to *joined* and calls *`complete`*`()` on all objects registered with
276
+ `*this`.
277
+
278
+ [*Note 1*: Calling *`complete`*`()` on any registered object can cause
279
+ `*this` to be destroyed. — *end note*]
280
+
281
+ ``` cpp
282
+ template<class State>
283
+ bool start-join-sender(State& st) noexcept;
284
+ ```
285
+
286
+ *Effects:* If *state* is
287
+
288
+ - *unused*, *unused-and-closed*, or *joined*, then changes *state* to
289
+ *joined* and returns `true`;
290
+ - *open* or *open-and-joining*, then changes *state* to
291
+ *open-and-joining*, registers `st` with `*this` and returns `false`;
292
+ - *closed* or *closed-and-joining*, then changes *state* to
293
+ *closed-and-joining*, registers `st` with `*this` and returns `false`.
294
+
295
+ ##### Token <a id="exec.simple.counting.token">[[exec.simple.counting.token]]</a>
296
+
297
+ ``` cpp
298
+ namespace std::execution {
299
+ struct simple_counting_scope::token {
300
+ template<sender Sender>
301
+ Sender&& wrap(Sender&& snd) const noexcept;
302
+ bool try_associate() const noexcept;
303
+ void disassociate() const noexcept;
304
+
305
+ private:
306
+ simple_counting_scope* scope; // exposition only
307
+ };
308
+ }
309
+ ```
310
+
311
+ ``` cpp
312
+ template<sender Sender>
313
+ Sender&& wrap(Sender&& snd) const noexcept;
314
+ ```
315
+
316
+ *Returns:* `std::forward<Sender>(snd)`.
317
+
318
+ ``` cpp
319
+ bool try_associate() const noexcept;
320
+ ```
321
+
322
+ *Effects:* Equivalent to: `return `*`scope`*`->`*`try-associate`*`();`
323
+
324
+ ``` cpp
325
+ void disassociate() const noexcept;
326
+ ```
327
+
328
+ *Effects:* Equivalent to *`scope`*`->`*`disassociate`*`()`.
329
+
330
+ #### Counting Scope <a id="exec.scope.counting">[[exec.scope.counting]]</a>
331
+
332
+ ``` cpp
333
+ namespace std::execution {
334
+ class counting_scope {
335
+ public:
336
+ struct token {
337
+ template<sender Sender>
338
+ sender auto wrap(Sender&& snd) const noexcept(see below);
339
+ bool try_associate() const noexcept;
340
+ void disassociate() const noexcept;
341
+
342
+ private:
343
+ counting_scope* scope; // exposition only
344
+ };
345
+
346
+ static constexpr size_t max_associations = implementation-defined;
347
+
348
+ counting_scope() noexcept;
349
+ counting_scope(counting_scope&&) = delete;
350
+ ~counting_scope();
351
+
352
+ token get_token() noexcept;
353
+ void close() noexcept;
354
+ sender auto join() noexcept;
355
+ void request_stop() noexcept;
356
+
357
+ private:
358
+ size_t count; // exposition only
359
+ scope-state-type state; // exposition only
360
+ inplace_stop_source s_source; // exposition only
361
+
362
+ bool try-associate() noexcept; // exposition only
363
+ void disassociate() noexcept; // exposition only
364
+
365
+ template<class State>
366
+ bool start-join-sender(State& state) noexcept; // exposition only
367
+ };
368
+ }
369
+ ```
370
+
371
+ `counting_scope` differs from `simple_counting_scope` by adding support
372
+ for cancellation. Unless specified below, the semantics of members of
373
+ `counting_scope` are the same as the corresponding members of
374
+ `simple_counting_scope`.
375
+
376
+ ``` cpp
377
+ token get_token() noexcept;
378
+ ```
379
+
380
+ *Returns:* An object `t` of type `counting_scope::token` such that
381
+ `t.`*`scope`*` == this` is `true`.
382
+
383
+ ``` cpp
384
+ void request_stop() noexcept;
385
+ ```
386
+
387
+ *Effects:* Equivalent to *`s_source`*`.request_stop()`.
388
+
389
+ *Remarks:* Calls to `request_stop` do not introduce data races.
390
+
391
+ ``` cpp
392
+ template<sender Sender>
393
+ sender auto counting_scope::token::wrap(Sender&& snd) const
394
+ noexcept(is_nothrow_constructible_v<remove_cvref_t<Sender>, Sender>);
395
+ ```
396
+
397
+ *Effects:* Equivalent to:
398
+
399
+ ``` cpp
400
+ return stop-when(std::forward<Sender>(snd), scope->s_source.get_token());
401
+ ```
402
+