Atomic types
Contents |
Syntax
_Atomic
(
type-name
)
|
(1) | (since C11) | |||||||
_Atomic
type-name
|
(2) | (since C11) | |||||||
const
,
volatile
, and
restrict
, although unlike other qualifiers, the atomic version of
type-name
may have a different size, alignment, and object representation.
| type-name | - | any type other than array or function. For (1) , type-name also cannot be atomic or cvr-qualified |
The header <stdatomic.h> defines many convenience type aliases , from atomic_bool to atomic_uintmax_t , which simplify the use of this keyword with built-in and library types.
_Atomic const int* p1; // p is a pointer to an atomic const int const atomic_int* p2; // same const _Atomic(int)* p3; // same
If the macro constant __STDC_NO_ATOMICS__ is defined by the compiler, the keyword _Atomic is not provided.
Explanation
Objects of atomic types are the only objects that are free from data races ; that is, they may be modified by two threads concurrently or modified by one and read by another.
Each atomic object has its own associated
modification order
, which is a total order of modifications made to that object. If, from some thread's point of view, modification
A
of some atomic
M
happens-before
modification
B
of the same atomic
M
, then in the modification order of
M
,
A
occurs before
B
.
Note that although each atomic object has its own modification order, there is no single total order; different threads may observe modifications to different atomic objects in different orders.
There are four coherence kinds that are guaranteed for all atomic operations:
-
write-write coherence
: If an operation
Athat modifies an atomic objectMhappens-before an operationBthat modifiesM, thenAappears earlier thanBin the modification order ofM. -
read-read coherence
: If a value computation
Aof an atomic objectMhappens before a value computationBofM, andAtakes its value from a side effectXonM, then the value computed byBis either the value stored byXor is the value stored by a side effectYonM, whereYappears later thanXin the modification order ofM. -
read-write coherence
: If a value computation
Aof an atomic objectMhappens-before an operationBonM, thenAtakes its value from a side effectXonM, whereXappears beforeBin the modification order ofM. -
write-read coherence
: If a side effect
Xon an atomic objectMhappens-before a value computationBofM, then the evaluationBtakes its value fromXor from a side effectYthat appears afterXin the modification order ofM.
Some atomic operations are also synchronization operations; they may have additional release semantics, acquire semantics, or sequentially-consistent semantics. See memory_order .
Built-in increment and decrement operators and compound assignment are read-modify-write atomic operations with total sequentially consistent ordering (as if using memory_order_seq_cst ). If less strict synchronization semantics are desired, the standard library functions may be used instead.
Atomic properties are only meaningful for lvalue expressions . Lvalue-to-rvalue conversion (which models a memory read from an atomic location to a CPU register) strips atomicity along with other qualifiers.
|
This section is incomplete
Reason: more, review interaction with memory_order and atomic library pages |
Notes
Accessing a member of an atomic struct/union is undefined behavior.
The library type sig_atomic_t does not provide inter-thread synchronization or memory ordering, only atomicity.
The
volatile
types do not provide inter-thread synchronization, memory ordering, or atomicity.
Implementations are recommended to ensure that the representation of
_Atomic
(
T
)
in C is same as that of
std
::
atomic
<
T
>
in C++ for every possible type
T
. The mechanisms used to ensure atomicity and memory ordering should be compatible.
Keywords
Example
#include <stdatomic.h> #include <stdio.h> #include <threads.h> atomic_int acnt; int cnt; int f(void* thr_data) { for (int n = 0; n < 1000; ++n) { ++cnt; ++acnt; // for this example, relaxed memory order is sufficient, e.g. // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed); } return 0; } int main(void) { thrd_t thr[10]; for (int n = 0; n < 10; ++n) thrd_create(&thr[n], f, NULL); for (int n = 0; n < 10; ++n) thrd_join(thr[n], NULL); printf("The atomic counter is %u\n", acnt); printf("The non-atomic counter is %u\n", cnt); }
Possible output:
The atomic counter is 10000 The non-atomic counter is 8644
References
- C23 standard (ISO/IEC 9899:2024):
-
- 6.7.2.4 Atomic type specifiers (p: TBD)
-
- 7.17 Atomics <stdatomic.h> (p: TBD)
- C17 standard (ISO/IEC 9899:2018):
-
- 6.7.2.4 Atomic type specifiers (p: 87)
-
- 7.17 Atomics <stdatomic.h> (p: 200-209)
- C11 standard (ISO/IEC 9899:2011):
-
- 6.7.2.4 Atomic type specifiers (p: 121)
-
- 7.17 Atomics <stdatomic.h> (p: 273-286)
See also
| Concurrency support library | |
|
C++ documentation
for
atomic
|