@@ -1497,6 +1497,22 @@ concurrency, including:
1497
1497
1498
1498
# Revision history # {#revisions}
1499
1499
1500
+ ## R10 ## {#r10}
1501
+
1502
+ The changes since R9 are as follows:
1503
+
1504
+ <b> Fixes:</b>
1505
+
1506
+
1507
+ <b> Enhancements:</b>
1508
+
1509
+ * The `get_delegatee_scheduler` query has been renamed to
1510
+ `get_delegation_scheduler`.
1511
+
1512
+ * An exposition-only <i> `simple-allocator`</i> concept is added to the
1513
+ Library introduction ([library] ), and the specification of the
1514
+ `get_allocator` query is expressed in terms of it.
1515
+
1500
1516
## R9 ## {#r9}
1501
1517
1502
1518
The changes since R8 are as follows:
@@ -3185,8 +3201,8 @@ execution::sender auto read(auto tag);
3185
3201
execution::sender auto get_scheduler() {
3186
3202
return read(execution::get_scheduler);
3187
3203
}
3188
- execution::sender auto get_delegatee_scheduler () {
3189
- return read(execution::get_delegatee_scheduler );
3204
+ execution::sender auto get_delegation_scheduler () {
3205
+ return read(execution::get_delegation_scheduler );
3190
3206
}
3191
3207
execution::sender auto get_allocator() {
3192
3208
return read(execution::get_allocator);
@@ -4020,6 +4036,72 @@ a new bullet as follows:</span>
4020
4036
</div>
4021
4037
</div>
4022
4038
4039
+ <div class="hidden">
4040
+ <h3 class="heading no-toc" id="hidden.d.2"> </h3>
4041
+ <h3 class="heading no-toc" id="hidden.d.3"> </h3>
4042
+ <h3 class="heading no-toc" id="hidden.d.4"> </h3>
4043
+ <h3 class="heading no-toc" id="hidden.d.4a"> </h3>
4044
+ <h4 class="heading no-toc" id="hidden.d.5"> </h4>
4045
+ <h4 class="heading no-toc" id="hidden.d.6"> </h4>
4046
+ <h4 class="heading no-toc" id="hidden.d.7"> </h4>
4047
+ <h4 class="heading no-toc" id="hidden.d.7a"> </h4>
4048
+ <h5 class="heading no-toc" id="hidden.d.8"> </h5>
4049
+ <h5 class="heading no-toc" id="hidden.d.9"> </h5>
4050
+ <h5 class="heading no-toc" id="hidden.d.10"> </h5>
4051
+ <h5 class="heading no-toc" id="hidden.d.11"> </h5>
4052
+ <h5 class="heading no-toc" id="hidden.d.12"> </h5>
4053
+ <h5 class="heading no-toc" id="hidden.d.13"> </h5>
4054
+ </div>
4055
+
4056
+
4057
+ <div class="standardeze">
4058
+ <h6 class="heading" id="allocator.requirements.general">General <b>[allocator.requirements.general]</b> </h6>
4059
+
4060
+ <span class="ed-note"> At the end of [allocator.requirements.general] , add the
4061
+ following new paragraph:</span>
4062
+
4063
+ 98. [*Example 2*: The following is an allocator class template supporting the
4064
+ minimal interface that meets the requirements of
4065
+ [allocator.requirements.general] :
4066
+
4067
+ <pre highlight="c++">
4068
+ template<class T>
4069
+ struct SimpleAllocator {
4070
+ using value_type = T;
4071
+ SimpleAllocator(<i> ctor args</i> );
4072
+
4073
+ template<class U> SimpleAllocator(const SimpleAllocator<U>& other);
4074
+
4075
+ T* allocate(std::size_t n);
4076
+ void deallocate(T* p, std::size_t n);
4077
+
4078
+ template<class U> bool operator==(const SimpleAllocator<U>& rhs) const;
4079
+ };
4080
+ </pre>
4081
+
4082
+ -- *end example*]
4083
+
4084
+ <div class="block-insert">
4085
+ 99. The following exposition-only concept defines the minimal requirements on an
4086
+ <i> Allocator</i> type.
4087
+
4088
+ <pre highlight="c++">
4089
+ template<class Alloc>
4090
+ concept <i> simple-allocator</i> =
4091
+ requires(Alloc alloc, size_t n) {
4092
+ { *alloc.allocate(n) } -> same_as<typename Alloc::value_type&>;
4093
+ { alloc.deallocate(alloc.allocate(n), n) };
4094
+ } &&
4095
+ copy_constructible<Alloc> &&
4096
+ equality_comparable<Alloc>;
4097
+ </pre>
4098
+
4099
+ 1. A type `Alloc` models <i> `simple-allocator`</i> if it meets the requirements of
4100
+ [allocator.requirements.general] .
4101
+
4102
+ </div>
4103
+ </div>
4104
+
4023
4105
<div class="hidden">
4024
4106
<h2 class="heading no-toc" id="hidden.e.1"> </h2>
4025
4107
<h2 class="heading no-toc" id="hidden.e.2"> </h2>
@@ -5252,7 +5334,7 @@ template<class Initializer>
5252
5334
<td>
5253
5335
<ul>
5254
5336
<li> general queries (e.g., `get_allocator`, `get_stop_token`)</li>
5255
- <li> environment queries (e.g., `get_scheduler`, `get_delegatee_scheduler `)</li>
5337
+ <li> environment queries (e.g., `get_scheduler`, `get_delegation_scheduler `)</li>
5256
5338
<li> scheduler queries (e.g., `get_forward_progress_guarantee`, `execute_may_block_caller`)</li>
5257
5339
<li> sender attribute queries (e.g., `get_completion_scheduler`)</li>
5258
5340
</ul>
@@ -5572,14 +5654,14 @@ namespace std::execution {
5572
5654
};
5573
5655
struct get_domain_t { <i> see below</i> };
5574
5656
struct get_scheduler_t { <i> see below</i> };
5575
- struct get_delegatee_scheduler_t { <i> see below</i> };
5657
+ struct get_delegation_scheduler_t { <i> see below</i> };
5576
5658
struct get_forward_progress_guarantee_t { <i> see below</i> };
5577
5659
template<class CPO>
5578
5660
struct get_completion_scheduler_t { <i> see below</i> };
5579
5661
5580
5662
inline constexpr get_domain_t get_domain{};
5581
5663
inline constexpr get_scheduler_t get_scheduler{};
5582
- inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler {};
5664
+ inline constexpr get_delegation_scheduler_t get_delegation_scheduler {};
5583
5665
inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{};
5584
5666
template<class CPO>
5585
5667
inline constexpr get_completion_scheduler_t<CPO> get_completion_scheduler{};
@@ -5849,12 +5931,9 @@ namespace std::execution {
5849
5931
}
5850
5932
</pre>
5851
5933
5852
-
5853
- <!-- LWG stopped here -->
5854
-
5855
5934
## Queries <b> [exec.queries] </b> ## {#spec-execution.queries}
5856
5935
5857
- ### `std:: forwarding_query` <b> [exec.fwd.env] </b> ### {#spec-execution.forwarding_query}
5936
+ ### `forwarding_query` <b> [exec.fwd.env] </b> ### {#spec-execution.forwarding_query}
5858
5937
5859
5938
1. `forwarding_query` asks a query object whether it should be forwarded
5860
5939
through queryable adaptors.
@@ -5867,33 +5946,35 @@ namespace std::execution {
5867
5946
expression is well-formed.
5868
5947
5869
5948
* <i> Mandates:</i> The expression above has type `bool` and is a core
5870
- constant expressions if `q` is a core constant expression.
5949
+ constant expression if `q` is a core constant expression.
5871
5950
5872
5951
2. Otherwise, `true` if `derived_from<Q, forwarding_query_t>` is
5873
5952
`true`.
5874
5953
5875
5954
3. Otherwise, `false`.
5876
5955
5877
- ### `std:: get_allocator` <b> [exec.get.allocator] </b> ### {#spec-execution.get_allocator}
5956
+ ### `get_allocator` <b> [exec.get.allocator] </b> ### {#spec-execution.get_allocator}
5878
5957
5879
- 1. `get_allocator` asks an object for its associated allocator.
5958
+ 1. `get_allocator` asks a queryable object for its associated allocator.
5880
5959
5881
5960
2. The name `get_allocator` denotes a query object. For a subexpression `env`,
5882
5961
`get_allocator(env)` is expression-equivalent to
5883
5962
<code><i> MANDATE-NOTHROW</i> (as_const(env).query(get_allocator))</code> .
5884
5963
5885
5964
* <i> Mandates:</i> If the expression above is well-formed, its type
5886
- satisfies <i> Allocator </i> .
5965
+ satisfies *`simple-allocator`* ( [allocator.requirements.general] ) .
5887
5966
5888
- 3. `forwarding_query(get_allocator)` is a core constant
5889
- expression and has value `true`.
5967
+ 3. `forwarding_query(get_allocator)` is a core constant expression and has value
5968
+ `true`.
5890
5969
5891
5970
4. `get_allocator()` (with no arguments) is expression-equivalent to
5892
5971
`execution::read(get_allocator)` ([exec.read] ).
5893
5972
5894
- ### `std::get_stop_token` <b> [exec.get.stop.token] </b> ### {#spec-execution.get_stop_token}
5895
5973
5896
- 1. `get_stop_token` asks an object for an associated stop token.
5974
+
5975
+ ### `get_stop_token` <b> [exec.get.stop.token] </b> ### {#spec-execution.get_stop_token}
5976
+
5977
+ 1. `get_stop_token` asks a queryable object for an associated stop token.
5897
5978
5898
5979
2. The name `get_stop_token` denotes a query object. For a subexpression `env`,
5899
5980
`get_stop_token(env)` is expression-equivalent to:
@@ -5917,23 +5998,23 @@ namespace std::execution {
5917
5998
1. `execution::get_env` is a customization point object. For a subexpression
5918
5999
`o`, `execution::get_env(o)` is expression-equivalent to:
5919
6000
5920
- 1. ` as_const(o).get_env()` if that expression is
6001
+ 1. <code><i> MANDATE-NOTHROW </i> ( as_const(o).get_env()) </code> if that expression is
5921
6002
well-formed.
5922
6003
5923
- * <i> Mandates:</i> The expression above is not potentially throwing, and
5924
- its type satisfies <i> `queryable`</i> ([exec.queryable] ).
6004
+ * <i> Mandates:</i> The type of the expression above satisfies
6005
+ <i> `queryable`</i> ([exec.queryable] ).
5925
6006
5926
6007
2. Otherwise, `empty_env{}`.
5927
6008
5928
6009
2. The value of `get_env(o)` shall be valid while `o` is valid.
5929
6010
5930
6011
3. <span class="wg21note"> When passed a sender object, `get_env` returns the
5931
- sender's attributes. When passed a receiver, `get_env` returns the
5932
- receiver's environment.</span>
6012
+ sender's associated attributes. When passed a receiver, `get_env` returns the
6013
+ receiver's associated execution environment.</span>
5933
6014
5934
6015
### `execution::get_domain` <b> [exec.get.domain] </b> ### {#spec-execution.get_domain}
5935
6016
5936
- 1. `get_domain` asks an object for an associated execution domain tag.
6017
+ 1. `get_domain` asks a queryable object for its associated execution domain tag.
5937
6018
5938
6019
2. The name `get_domain` denotes a query object. For a subexpression `env`,
5939
6020
`get_domain(env)` is expression-equivalent to
@@ -5947,7 +6028,7 @@ namespace std::execution {
5947
6028
5948
6029
### `execution::get_scheduler` <b> [exec.get.scheduler] </b> ### {#spec-execution.get_scheduler}
5949
6030
5950
- 1. `get_scheduler` asks an object for its associated scheduler.
6031
+ 1. `get_scheduler` asks a queryable object for its associated scheduler.
5951
6032
5952
6033
2. The name `get_scheduler` denotes a query object. For a
5953
6034
subexpression `env`, `get_scheduler(env)` is expression-equivalent to
@@ -5962,23 +6043,24 @@ namespace std::execution {
5962
6043
4. `get_scheduler()` (with no arguments) is expression-equivalent to
5963
6044
`execution::read(get_scheduler)` ([exec.read] ).
5964
6045
5965
- ### `execution::get_delegatee_scheduler ` <b> [exec.get.delegatee .scheduler] </b> ### {#spec-execution.get_delegatee_scheduler }
6046
+ ### `execution::get_delegation_scheduler ` <b> [exec.get.delegation .scheduler] </b> ### {#spec-execution.get_delegation_scheduler }
5966
6047
5967
- 1. `get_delegatee_scheduler` asks an object for a scheduler that can be used to
5968
- delegate work to for the purpose of forward progress delegation.
6048
+ 1. `get_delegation_scheduler` asks a queryable object for a scheduler that can be
6049
+ used to delegate work to for the purpose of forward progress delegation
6050
+ ([intro.progress] ).
5969
6051
5970
- 2. The name `get_delegatee_scheduler ` denotes a query object. For a
5971
- subexpression `env`, `get_delegatee_scheduler (env)` is expression-equivalent to
5972
- <code><i> MANDATE-NOTHROW</i> (as_const(env).query(get_delegatee_scheduler ))</code> .
6052
+ 2. The name `get_delegation_scheduler ` denotes a query object. For a
6053
+ subexpression `env`, `get_delegation_scheduler (env)` is expression-equivalent to
6054
+ <code><i> MANDATE-NOTHROW</i> (as_const(env).query(get_delegation_scheduler ))</code> .
5973
6055
5974
6056
* <i> Mandates:</i> If the expression above is well-formed, its type
5975
6057
satisfies `scheduler`.
5976
6058
5977
- 3. `forwarding_query(execution::get_delegatee_scheduler )` is a core
6059
+ 3. `forwarding_query(execution::get_delegation_scheduler )` is a core
5978
6060
constant expression and has value `true`.
5979
6061
5980
- 4. `get_delegatee_scheduler ()` (with no arguments) is expression-equivalent to
5981
- `execution::read(get_delegatee_scheduler )` ([exec.read] ).
6062
+ 4. `get_delegation_scheduler ()` (with no arguments) is expression-equivalent to
6063
+ `execution::read(get_delegation_scheduler )` ([exec.read] ).
5982
6064
5983
6065
### `execution::get_forward_progress_guarantee` <b> [exec.get.forward.progress.guarantee] </b> ### {#spec-execution.get_forward_progress_guarantee}
5984
6066
@@ -5993,7 +6075,8 @@ namespace std::execution {
5993
6075
</pre>
5994
6076
5995
6077
1. `get_forward_progress_guarantee` asks a scheduler about the forward progress
5996
- guarantee of execution agents created by that scheduler.
6078
+ guarantee of execution agents created by that scheduler's associated
6079
+ execution resource ([intro.progress] ).
5997
6080
5998
6081
2. The name `get_forward_progress_guarantee` denotes a query object. For a
5999
6082
subexpression `sch`, let `Sch` be `decltype((sch))`. If `Sch` does not
@@ -6002,7 +6085,7 @@ namespace std::execution {
6002
6085
to:
6003
6086
6004
6087
1. <code><i> MANDATE-NOTHROW</i> (as_const(sch).query(get_forward_progress_guarantee))</code> ,
6005
- if this expression is well-formed.
6088
+ if that expression is well-formed.
6006
6089
6007
6090
* <i> Mandates:</i> The type of the expression above is
6008
6091
`forward_progress_guarantee`.
@@ -6011,33 +6094,33 @@ namespace std::execution {
6011
6094
6012
6095
3. If `get_forward_progress_guarantee(sch)` for some scheduler `sch` returns
6013
6096
`forward_progress_guarantee::concurrent`, all execution agents created by
6014
- that scheduler shall provide the concurrent forward progress guarantee. If
6015
- it returns `forward_progress_guarantee::parallel`, all execution agents
6016
- created by that scheduler shall provide at least the parallel forward
6017
- progress guarantee.
6097
+ that scheduler's associated execution resource shall provide the concurrent
6098
+ forward progress guarantee. If it returns
6099
+ `forward_progress_guarantee::parallel`, all such execution agents shall
6100
+ provide at least the parallel forward progress guarantee.
6018
6101
6019
6102
### `this_thread::execute_may_block_caller` <b> [exec.execute.may.block.caller] </b> ### {#spec-execution.execute_may_block_caller}
6020
6103
6021
- 1. `this_thread:: execute_may_block_caller` asks a scheduler `sch` whether a call
6022
- `execute(sch, f)` with any invocable `f ` may block the thread where such a
6023
- call occurs .
6104
+ 1. `execute_may_block_caller` asks a scheduler `sch` whether any invocation of
6105
+ the `execute` algorithm ( [exec.execute] ) with `sch ` may block the current
6106
+ thread of execution ( [defns.block] ) .
6024
6107
6025
- 2. The name `this_thread:: execute_may_block_caller` denotes a query object. For
6108
+ 2. The name `execute_may_block_caller` denotes a query object. For
6026
6109
a subexpression `sch`, let `Sch` be `decltype((sch))`. If `Sch` does not
6027
- satisfy `scheduler`, `this_thread:: execute_may_block_caller` is ill-formed.
6028
- Otherwise, `this_thread:: execute_may_block_caller(sch)` is
6110
+ satisfy `scheduler`, `execute_may_block_caller(sch) ` is ill-formed.
6111
+ Otherwise, `execute_may_block_caller(sch)` is
6029
6112
expression-equivalent to:
6030
6113
6031
- 1. <code><i> MANDATE-NOTHROW</i> (as_const(sch).query(this_thread:: execute_may_block_caller))</code> ,
6032
- if this expression is well-formed.
6114
+ 1. <code><i> MANDATE-NOTHROW</i> (as_const(sch).query(execute_may_block_caller))</code> ,
6115
+ if that expression is well-formed.
6033
6116
6034
6117
* <i> Mandates:</i> The type of the expression above is `bool`.
6035
6118
6036
6119
2. Otherwise, `true`.
6037
6120
6038
- 3. If `this_thread:: execute_may_block_caller(sch)` for some scheduler `sch`
6039
- returns `false`, no `execute(sch, f)` call with some invocable `f ` shall
6040
- block the calling thread.
6121
+ 3. If `execute_may_block_caller(sch)` returns `false` for some scheduler `sch`,
6122
+ no invocation of the `execute` algorithm with `sch ` shall block the calling
6123
+ thread.
6041
6124
6042
6125
### `execution::get_completion_scheduler` <b> [exec.completion.scheduler] </b> ### {#spec-execution.get_completion_scheduler}
6043
6126
@@ -6046,27 +6129,33 @@ namespace std::execution {
6046
6129
attributes.
6047
6130
6048
6131
2. The name `get_completion_scheduler` denotes a query object template. For a
6049
- subexpression `q`, let `Q` be `decltype((q))`. If the template argument
6050
- `Tag` in `get_completion_scheduler<Tag>(q)` is not one of `set_value_t`,
6051
- `set_error_t`, or `set_stopped_t`, `get_completion_scheduler<Tag>(q)` is
6052
- ill-formed. Otherwise, `get_completion_scheduler<Tag>(q)` is
6132
+ subexpression `q`, the expression
6133
+ <code> get_completion_scheduler<<i> completion-tag</i> >(q)</code> is
6134
+ ill-formed if <i> `completion-tag`</i> is not one of `set_value_t`,
6135
+ `set_error_t`, or `set_stopped_t`. Otherwise,
6136
+ <code> get_completion_scheduler<<i> completion-tag</i> >(q)</code> is
6053
6137
expression-equivalent to
6054
- <code><i> MANDATE-NOTHROW</i> (as_const(q).query(get_completion_scheduler<Tag >))</code> .
6138
+ <code><i> MANDATE-NOTHROW</i> (as_const(q).query(get_completion_scheduler<<i> completion-tag </i> >))</code> .
6055
6139
6056
6140
* <i> Mandates:</i> If the expression above is well-formed, its type
6057
6141
satisfies `scheduler`.
6058
6142
6059
- 3. If, for some sender `sndr` and completion function `C` that has an associated
6060
- completion tag `Tag`, `get_completion_scheduler<Tag>(get_env(sndr))` is
6061
- well-formed and results in a scheduler `sch`, and the sender `sndr` invokes
6062
- `C(rcvr, args...)`, for some receiver `rcvr` that has been connected to `sndr`, with
6063
- additional arguments `args...`, on an execution agent that does not
6064
- belong to the associated execution resource of `sch`, the behavior is
6065
- undefined.
6143
+ 3. Let <i> `completion-fn`</i> be a completion function ([async.ops] ); let
6144
+ <i> `completion-tag`</i> be the associated completion tag of
6145
+ <i> `completion-fn`</i> ; let `args` be a pack of subexpressions; and let
6146
+ `sndr` be a subexpression such that `sender<decltype((sndr))> ` is `true` and
6147
+ <code> get_completion_scheduler<<i> completion-tag</i> >(get_env(sndr))</code>
6148
+ is well-formed and denotes a scheduler `sch`. If an asynchronous operation
6149
+ created by connecting `sndr` with a receiver `rcvr` causes the evaluation of
6150
+ <code><i> completion-fn</i> (rcvr, args...)</code> , the behavior is undefined
6151
+ unless it happens on an execution agent that belongs to `sch`'s associated
6152
+ execution resource.
6066
6153
6067
- 4. The expression ` forwarding_query(get_completion_scheduler<CPO>)`
6154
+ 4. The expression <code> forwarding_query(get_completion_scheduler< <i> completion-tag </i> >) </code>
6068
6155
is a core constant expression and has value `true`.
6069
6156
6157
+ <!-- LWG stopped here -->
6158
+
6070
6159
## Schedulers <b> [exec.sched] </b> ## {#spec-execution.schedulers}
6071
6160
6072
6161
1. The `scheduler` concept defines the requirements of a scheduler type
@@ -8857,7 +8946,7 @@ namespace std::execution {
8857
8946
execution::run_loop* <em> loop</em> ; // <i> exposition only</i>
8858
8947
8859
8948
auto query(execution::get_scheduler_t) const noexcept { <em> loop</em> ->get_scheduler(); }
8860
- auto query(execution::get_delegatee_scheduler_t ) const noexcept { <em> loop</em> ->get_scheduler(); }
8949
+ auto query(execution::get_delegation_scheduler_t ) const noexcept { <em> loop</em> ->get_scheduler(); }
8861
8950
};
8862
8951
}
8863
8952
</pre>
@@ -8987,7 +9076,7 @@ namespace std::execution {
8987
9076
progress guarantee delegation ([intro.progress] ) until the specified
8988
9077
sender completes. <span class="wg21note"> The default implementation of
8989
9078
`sync_wait` achieves forward progress guarantee delegation by providing
8990
- a `run_loop` scheduler via the `get_delegatee_scheduler ` query on the
9079
+ a `run_loop` scheduler via the `get_delegation_scheduler ` query on the
8991
9080
<i> `sync-wait-receiver`</i> 's environment. The `run_loop` is
8992
9081
driven by the current thread of execution.</span>
8993
9082
0 commit comments