From Jason Turner

[exec.counting.scopes]

Diff to HTML by rtfpessoa

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