From Jason Turner

[atomics.types.operations]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmpxu4rcz14/{from.md → to.md} +169 -86
tmp/tmpxu4rcz14/{from.md → to.md} RENAMED
@@ -1,64 +1,38 @@
1
  ### Operations on atomic types <a id="atomics.types.operations">[[atomics.types.operations]]</a>
2
 
3
- [*Note 1*: Many operations are volatile-qualified. The “volatile as
4
- device register” semantics have not changed in the standard. This
5
- qualification means that volatility is preserved when applying these
6
- operations to volatile objects. It does not mean that operations on
7
- non-volatile objects become volatile. — *end note*]
8
-
9
  ``` cpp
10
- atomic() noexcept = default;
11
  ```
12
 
13
- *Effects:* Leaves the atomic object in an uninitialized state.
14
 
15
- [*Note 1*: These semantics ensure compatibility with C. *end note*]
 
16
 
17
  ``` cpp
18
  constexpr atomic(T desired) noexcept;
19
  ```
20
 
21
  *Effects:* Initializes the object with the value `desired`.
22
- Initialization is not an atomic operation ([[intro.multithread]]).
23
 
24
- [*Note 2*: It is possible to have an access to an atomic object `A`
25
  race with its construction, for example by communicating the address of
26
  the just-constructed object `A` to another thread via
27
- `memory_order_relaxed` operations on a suitable atomic pointer variable,
28
- and then immediately accessing `A` in the receiving thread. This results
29
- in undefined behavior. — *end note*]
30
-
31
- ``` cpp
32
- #define ATOMIC_VAR_INIT(value) see below
33
- ```
34
-
35
- The macro expands to a token sequence suitable for constant
36
- initialization of an atomic variable of static storage duration of a
37
- type that is initialization-compatible with `value`.
38
-
39
- [*Note 3*: This operation may need to initialize locks. — *end note*]
40
-
41
- Concurrent access to the variable being initialized, even via an atomic
42
- operation, constitutes a data race.
43
-
44
- [*Example 1*:
45
-
46
- ``` cpp
47
- atomic<int> v = ATOMIC_VAR_INIT(5);
48
- ```
49
-
50
- — *end example*]
51
 
52
  ``` cpp
53
  static constexpr bool is_always_lock_free = implementation-defined // whether a given atomic type's operations are always lock free;
54
  ```
55
 
56
  The `static` data member `is_always_lock_free` is `true` if the atomic
57
  type’s operations are always lock-free, and `false` otherwise.
58
 
59
- [*Note 4*: The value of `is_always_lock_free` is consistent with the
60
  value of the corresponding `ATOMIC_..._LOCK_FREE` macro, if
61
  defined. — *end note*]
62
 
63
  ``` cpp
64
  bool is_lock_free() const volatile noexcept;
@@ -66,63 +40,79 @@ bool is_lock_free() const noexcept;
66
  ```
67
 
68
  *Returns:* `true` if the object’s operations are lock-free, `false`
69
  otherwise.
70
 
71
- [*Note 5*: The return value of the `is_lock_free` member function is
72
  consistent with the value of `is_always_lock_free` for the same
73
  type. — *end note*]
74
 
75
  ``` cpp
76
- void store(T desired, memory_order order = memory_order_seq_cst) volatile noexcept;
77
- void store(T desired, memory_order order = memory_order_seq_cst) noexcept;
78
  ```
79
 
80
- *Requires:* The `order` argument shall not be `memory_order_consume`,
81
- `memory_order_acquire`, nor `memory_order_acq_rel`.
 
 
 
 
82
 
83
  *Effects:* Atomically replaces the value pointed to by `this` with the
84
  value of `desired`. Memory is affected according to the value of
85
  `order`.
86
 
87
  ``` cpp
88
  T operator=(T desired) volatile noexcept;
89
  T operator=(T desired) noexcept;
90
  ```
91
 
92
- *Effects:* Equivalent to: `store(desired)`.
 
 
 
93
 
94
  *Returns:* `desired`.
95
 
96
  ``` cpp
97
- T load(memory_order order = memory_order_seq_cst) const volatile noexcept;
98
- T load(memory_order order = memory_order_seq_cst) const noexcept;
99
  ```
100
 
101
- *Requires:* The `order` argument shall not be `memory_order_release` nor
102
- `memory_order_acq_rel`.
 
 
 
103
 
104
  *Effects:* Memory is affected according to the value of `order`.
105
 
106
  *Returns:* Atomically returns the value pointed to by `this`.
107
 
108
  ``` cpp
109
  operator T() const volatile noexcept;
110
  operator T() const noexcept;
111
  ```
112
 
 
 
 
113
  *Effects:* Equivalent to: `return load();`
114
 
115
  ``` cpp
116
- T exchange(T desired, memory_order order = memory_order_seq_cst) volatile noexcept;
117
- T exchange(T desired, memory_order order = memory_order_seq_cst) noexcept;
118
  ```
119
 
 
 
 
120
  *Effects:* Atomically replaces the value pointed to by `this` with
121
  `desired`. Memory is affected according to the value of `order`. These
122
  operations are atomic read-modify-write
123
- operations ([[intro.multithread]]).
124
 
125
  *Returns:* Atomically returns the value pointed to by `this` immediately
126
  before the effects.
127
 
128
  ``` cpp
@@ -133,57 +123,60 @@ bool compare_exchange_weak(T& expected, T desired,
133
  bool compare_exchange_strong(T& expected, T desired,
134
  memory_order success, memory_order failure) volatile noexcept;
135
  bool compare_exchange_strong(T& expected, T desired,
136
  memory_order success, memory_order failure) noexcept;
137
  bool compare_exchange_weak(T& expected, T desired,
138
- memory_order order = memory_order_seq_cst) volatile noexcept;
139
  bool compare_exchange_weak(T& expected, T desired,
140
- memory_order order = memory_order_seq_cst) noexcept;
141
  bool compare_exchange_strong(T& expected, T desired,
142
- memory_order order = memory_order_seq_cst) volatile noexcept;
143
  bool compare_exchange_strong(T& expected, T desired,
144
- memory_order order = memory_order_seq_cst) noexcept;
145
  ```
146
 
147
- *Requires:* The `failure` argument shall not be `memory_order_release`
148
- nor `memory_order_acq_rel`.
 
 
 
149
 
150
  *Effects:* Retrieves the value in `expected`. It then atomically
151
- compares the contents of the memory pointed to by `this` for equality
152
- with that previously retrieved from `expected`, and if true, replaces
153
- the contents of the memory pointed to by `this` with that in `desired`.
154
- If and only if the comparison is true, memory is affected according to
155
- the value of `success`, and if the comparison is false, memory is
156
- affected according to the value of `failure`. When only one
157
- `memory_order` argument is supplied, the value of `success` is `order`,
158
- and the value of `failure` is `order` except that a value of
159
- `memory_order_acq_rel` shall be replaced by the value
160
- `memory_order_acquire` and a value of `memory_order_release` shall be
161
- replaced by the value `memory_order_relaxed`. If and only if the
162
- comparison is false then, after the atomic operation, the contents of
163
- the memory in `expected` are replaced by the value read from the memory
164
- pointed to by `this` during the atomic comparison. If the operation
165
- returns `true`, these operations are atomic read-modify-write
166
- operations ([[intro.multithread]]) on the memory pointed to by `this`.
167
  Otherwise, these operations are atomic load operations on that memory.
168
 
169
  *Returns:* The result of the comparison.
170
 
171
- [*Note 6*:
172
 
173
- For example, the effect of `compare_exchange_strong` is
 
174
 
175
  ``` cpp
176
  if (memcmp(this, &expected, sizeof(*this)) == 0)
177
  memcpy(this, &desired, sizeof(*this));
178
  else
179
  memcpy(expected, this, sizeof(*this));
180
  ```
181
 
182
  — *end note*]
183
 
184
- [*Example 2*:
185
 
186
  The expected use of the compare-and-exchange operations is as follows.
187
  The compare-and-exchange operations will update `expected` when another
188
  iteration of the loop is needed.
189
 
@@ -194,16 +187,16 @@ do {
194
  } while (!current.compare_exchange_weak(expected, desired));
195
  ```
196
 
197
  — *end example*]
198
 
199
- [*Example 3*:
200
 
201
  Because the expected value is updated only on failure, code releasing
202
- the memory containing the `expected` value on success will work. E.g.
203
- list head insertion will act atomically and would not introduce a data
204
- race in the following code:
205
 
206
  ``` cpp
207
  do {
208
  p->next = head; // make new list node point to the current head
209
  } while (!head.compare_exchange_weak(p->next, p)); // try to insert
@@ -219,22 +212,112 @@ the atomic object.
219
  *Remarks:* A weak compare-and-exchange operation may fail spuriously.
220
  That is, even when the contents of memory referred to by `expected` and
221
  `this` are equal, it may return `false` and store back to `expected` the
222
  same memory contents that were originally there.
223
 
224
- [*Note 7*: This spurious failure enables implementation of
225
  compare-and-exchange on a broader class of machines, e.g., load-locked
226
  store-conditional machines. A consequence of spurious failure is that
227
  nearly all uses of weak compare-and-exchange will be in a loop. When a
228
  compare-and-exchange is in a loop, the weak version will yield better
229
  performance on some platforms. When a weak compare-and-exchange would
230
  require a loop and a strong one would not, the strong one is
231
  preferable. — *end note*]
232
 
233
- [*Note 8*: The `memcpy` and `memcmp` semantics of the
234
- compare-and-exchange operations may result in failed comparisons for
235
- values that compare equal with `operator==` if the underlying type has
236
- padding bits, trap bits, or alternate representations of the same value.
237
- Thus, `compare_exchange_strong` should be used with extreme care. On the
238
- other hand, `compare_exchange_weak` should converge
239
- rapidly. *end note*]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
 
1
  ### Operations on atomic types <a id="atomics.types.operations">[[atomics.types.operations]]</a>
2
 
 
 
 
 
 
 
3
  ``` cpp
4
+ constexpr atomic() noexcept(is_nothrow_default_constructible_v<T>);
5
  ```
6
 
7
+ *Mandates:* `is_default_constructible_v<T>` is `true`.
8
 
9
+ *Effects:* Initializes the atomic object with the value of `T()`.
10
+ Initialization is not an atomic operation [[intro.multithread]].
11
 
12
  ``` cpp
13
  constexpr atomic(T desired) noexcept;
14
  ```
15
 
16
  *Effects:* Initializes the object with the value `desired`.
17
+ Initialization is not an atomic operation [[intro.multithread]].
18
 
19
+ [*Note 1*: It is possible to have an access to an atomic object `A`
20
  race with its construction, for example by communicating the address of
21
  the just-constructed object `A` to another thread via
22
+ `memory_order::relaxed` operations on a suitable atomic pointer
23
+ variable, and then immediately accessing `A` in the receiving thread.
24
+ This results in undefined behavior. — *end note*]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  ``` cpp
27
  static constexpr bool is_always_lock_free = implementation-defined // whether a given atomic type's operations are always lock free;
28
  ```
29
 
30
  The `static` data member `is_always_lock_free` is `true` if the atomic
31
  type’s operations are always lock-free, and `false` otherwise.
32
 
33
+ [*Note 2*: The value of `is_always_lock_free` is consistent with the
34
  value of the corresponding `ATOMIC_..._LOCK_FREE` macro, if
35
  defined. — *end note*]
36
 
37
  ``` cpp
38
  bool is_lock_free() const volatile noexcept;
 
40
  ```
41
 
42
  *Returns:* `true` if the object’s operations are lock-free, `false`
43
  otherwise.
44
 
45
+ [*Note 3*: The return value of the `is_lock_free` member function is
46
  consistent with the value of `is_always_lock_free` for the same
47
  type. — *end note*]
48
 
49
  ``` cpp
50
+ void store(T desired, memory_order order = memory_order::seq_cst) volatile noexcept;
51
+ void store(T desired, memory_order order = memory_order::seq_cst) noexcept;
52
  ```
53
 
54
+ *Preconditions:* The `order` argument is neither
55
+ `memory_order::consume`, `memory_order::acquire`, nor
56
+ `memory_order::acq_rel`.
57
+
58
+ *Constraints:* For the `volatile` overload of this function,
59
+ `is_always_lock_free` is `true`.
60
 
61
  *Effects:* Atomically replaces the value pointed to by `this` with the
62
  value of `desired`. Memory is affected according to the value of
63
  `order`.
64
 
65
  ``` cpp
66
  T operator=(T desired) volatile noexcept;
67
  T operator=(T desired) noexcept;
68
  ```
69
 
70
+ *Constraints:* For the `volatile` overload of this function,
71
+ `is_always_lock_free` is `true`.
72
+
73
+ *Effects:* Equivalent to `store(desired)`.
74
 
75
  *Returns:* `desired`.
76
 
77
  ``` cpp
78
+ T load(memory_order order = memory_order::seq_cst) const volatile noexcept;
79
+ T load(memory_order order = memory_order::seq_cst) const noexcept;
80
  ```
81
 
82
+ *Preconditions:* The `order` argument is neither `memory_order::release`
83
+ nor `memory_order::acq_rel`.
84
+
85
+ *Constraints:* For the `volatile` overload of this function,
86
+ `is_always_lock_free` is `true`.
87
 
88
  *Effects:* Memory is affected according to the value of `order`.
89
 
90
  *Returns:* Atomically returns the value pointed to by `this`.
91
 
92
  ``` cpp
93
  operator T() const volatile noexcept;
94
  operator T() const noexcept;
95
  ```
96
 
97
+ *Constraints:* For the `volatile` overload of this function,
98
+ `is_always_lock_free` is `true`.
99
+
100
  *Effects:* Equivalent to: `return load();`
101
 
102
  ``` cpp
103
+ T exchange(T desired, memory_order order = memory_order::seq_cst) volatile noexcept;
104
+ T exchange(T desired, memory_order order = memory_order::seq_cst) noexcept;
105
  ```
106
 
107
+ *Constraints:* For the `volatile` overload of this function,
108
+ `is_always_lock_free` is `true`.
109
+
110
  *Effects:* Atomically replaces the value pointed to by `this` with
111
  `desired`. Memory is affected according to the value of `order`. These
112
  operations are atomic read-modify-write
113
+ operations [[intro.multithread]].
114
 
115
  *Returns:* Atomically returns the value pointed to by `this` immediately
116
  before the effects.
117
 
118
  ``` cpp
 
123
  bool compare_exchange_strong(T& expected, T desired,
124
  memory_order success, memory_order failure) volatile noexcept;
125
  bool compare_exchange_strong(T& expected, T desired,
126
  memory_order success, memory_order failure) noexcept;
127
  bool compare_exchange_weak(T& expected, T desired,
128
+ memory_order order = memory_order::seq_cst) volatile noexcept;
129
  bool compare_exchange_weak(T& expected, T desired,
130
+ memory_order order = memory_order::seq_cst) noexcept;
131
  bool compare_exchange_strong(T& expected, T desired,
132
+ memory_order order = memory_order::seq_cst) volatile noexcept;
133
  bool compare_exchange_strong(T& expected, T desired,
134
+ memory_order order = memory_order::seq_cst) noexcept;
135
  ```
136
 
137
+ *Preconditions:* The `failure` argument is neither
138
+ `memory_order::release` nor `memory_order::acq_rel`.
139
+
140
+ *Constraints:* For the `volatile` overload of this function,
141
+ `is_always_lock_free` is `true`.
142
 
143
  *Effects:* Retrieves the value in `expected`. It then atomically
144
+ compares the value representation of the value pointed to by `this` for
145
+ equality with that previously retrieved from `expected`, and if true,
146
+ replaces the value pointed to by `this` with that in `desired`. If and
147
+ only if the comparison is `true`, memory is affected according to the
148
+ value of `success`, and if the comparison is false, memory is affected
149
+ according to the value of `failure`. When only one `memory_order`
150
+ argument is supplied, the value of `success` is `order`, and the value
151
+ of `failure` is `order` except that a value of `memory_order::acq_rel`
152
+ shall be replaced by the value `memory_order::acquire` and a value of
153
+ `memory_order::release` shall be replaced by the value
154
+ `memory_order::relaxed`. If and only if the comparison is false then,
155
+ after the atomic operation, the value in `expected` is replaced by the
156
+ value pointed to by `this` during the atomic comparison. If the
157
+ operation returns `true`, these operations are atomic read-modify-write
158
+ operations [[intro.multithread]] on the memory pointed to by `this`.
 
159
  Otherwise, these operations are atomic load operations on that memory.
160
 
161
  *Returns:* The result of the comparison.
162
 
163
+ [*Note 4*:
164
 
165
+ For example, the effect of `compare_exchange_strong` on objects without
166
+ padding bits [[basic.types]] is
167
 
168
  ``` cpp
169
  if (memcmp(this, &expected, sizeof(*this)) == 0)
170
  memcpy(this, &desired, sizeof(*this));
171
  else
172
  memcpy(expected, this, sizeof(*this));
173
  ```
174
 
175
  — *end note*]
176
 
177
+ [*Example 1*:
178
 
179
  The expected use of the compare-and-exchange operations is as follows.
180
  The compare-and-exchange operations will update `expected` when another
181
  iteration of the loop is needed.
182
 
 
187
  } while (!current.compare_exchange_weak(expected, desired));
188
  ```
189
 
190
  — *end example*]
191
 
192
+ [*Example 2*:
193
 
194
  Because the expected value is updated only on failure, code releasing
195
+ the memory containing the `expected` value on success will work. For
196
+ example, list head insertion will act atomically and would not introduce
197
+ a data race in the following code:
198
 
199
  ``` cpp
200
  do {
201
  p->next = head; // make new list node point to the current head
202
  } while (!head.compare_exchange_weak(p->next, p)); // try to insert
 
212
  *Remarks:* A weak compare-and-exchange operation may fail spuriously.
213
  That is, even when the contents of memory referred to by `expected` and
214
  `this` are equal, it may return `false` and store back to `expected` the
215
  same memory contents that were originally there.
216
 
217
+ [*Note 5*: This spurious failure enables implementation of
218
  compare-and-exchange on a broader class of machines, e.g., load-locked
219
  store-conditional machines. A consequence of spurious failure is that
220
  nearly all uses of weak compare-and-exchange will be in a loop. When a
221
  compare-and-exchange is in a loop, the weak version will yield better
222
  performance on some platforms. When a weak compare-and-exchange would
223
  require a loop and a strong one would not, the strong one is
224
  preferable. — *end note*]
225
 
226
+ [*Note 6*: Under cases where the `memcpy` and `memcmp` semantics of the
227
+ compare-and-exchange operations apply, the outcome might be failed
228
+ comparisons for values that compare equal with `operator==` if the value
229
+ representation has trap bits or alternate representations of the same
230
+ value. Notably, on implementations conforming to ISO/IEC/IEEE 60559,
231
+ floating-point `-0.0` and `+0.0` will not compare equal with `memcmp`
232
+ but will compare equal with `operator==`, and NaNs with the same payload
233
+ will compare equal with `memcmp` but will not compare equal with
234
+ `operator==`. — *end note*]
235
+
236
+ [*Note 7*:
237
+
238
+ Because compare-and-exchange acts on an object’s value representation,
239
+ padding bits that never participate in the object’s value representation
240
+ are ignored. As a consequence, the following code is guaranteed to avoid
241
+ spurious failure:
242
+
243
+ ``` cpp
244
+ struct padded {
245
+ char clank = 0x42;
246
+ // Padding here.
247
+ unsigned biff = 0xC0DEFEFE;
248
+ };
249
+ atomic<padded> pad = {};
250
+
251
+ bool zap() {
252
+ padded expected, desired{0, 0};
253
+ return pad.compare_exchange_strong(expected, desired);
254
+ }
255
+ ```
256
+
257
+ — *end note*]
258
+
259
+ [*Note 8*:
260
+
261
+ For a union with bits that participate in the value representation of
262
+ some members but not others, compare-and-exchange might always fail.
263
+ This is because such padding bits have an indeterminate value when they
264
+ do not participate in the value representation of the active member. As
265
+ a consequence, the following code is not guaranteed to ever succeed:
266
+
267
+ ``` cpp
268
+ union pony {
269
+ double celestia = 0.;
270
+ short luna; // padded
271
+ };
272
+ atomic<pony> princesses = {};
273
+
274
+ bool party(pony desired) {
275
+ pony expected;
276
+ return princesses.compare_exchange_strong(expected, desired);
277
+ }
278
+ ```
279
+
280
+ — *end note*]
281
+
282
+ ``` cpp
283
+ void wait(T old, memory_order order = memory_order::seq_cst) const volatile noexcept;
284
+ void wait(T old, memory_order order = memory_order::seq_cst) const noexcept;
285
+ ```
286
+
287
+ *Preconditions:* `order` is neither `memory_order::release` nor
288
+ `memory_order::acq_rel`.
289
+
290
+ *Effects:* Repeatedly performs the following steps, in order:
291
+
292
+ - Evaluates `load(order)` and compares its value representation for
293
+ equality against that of `old`.
294
+ - If they compare unequal, returns.
295
+ - Blocks until it is unblocked by an atomic notifying operation or is
296
+ unblocked spuriously.
297
+
298
+ *Remarks:* This function is an atomic waiting
299
+ operation [[atomics.wait]].
300
+
301
+ ``` cpp
302
+ void notify_one() volatile noexcept;
303
+ void notify_one() noexcept;
304
+ ```
305
+
306
+ *Effects:* Unblocks the execution of at least one atomic waiting
307
+ operation that is eligible to be unblocked [[atomics.wait]] by this
308
+ call, if any such atomic waiting operations exist.
309
+
310
+ *Remarks:* This function is an atomic notifying
311
+ operation [[atomics.wait]].
312
+
313
+ ``` cpp
314
+ void notify_all() volatile noexcept;
315
+ void notify_all() noexcept;
316
+ ```
317
+
318
+ *Effects:* Unblocks the execution of all atomic waiting operations that
319
+ are eligible to be unblocked [[atomics.wait]] by this call.
320
+
321
+ *Remarks:* This function is an atomic notifying
322
+ operation [[atomics.wait]].
323