This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.
atomic
's default constructor requires "uninitialized" state even for types with non-trivial default-constructorSection: 33.5.8.2 [atomics.types.operations] Status: Resolved Submitter: Daniel Krügler Opened: 2013-10-03 Last modified: 2019-11-19
Priority: Not Prioritized
View all other issues in [atomics.types.operations].
View all issues with Resolved status.
Discussion:
According to 99 [atomics.types.operations.req] p4,
A::A() noexcept = default;Effects: leaves the atomic object in an uninitialized state. [Note: These semantics ensure compatibility with
C
. — end note]
This implementation requirement is OK for POD types, like int
, but 33.5.8 [atomics.types.generic] p1
intentionally allows template arguments of atomic
with a non-trivial default constructor ("The type of the template argument
T
shall be trivially copyable (3.9)"), so this wording can be read in a way that makes the behaviour of the following code
undefined:
#include <atomic> #include <iostream> struct S { S() noexcept : v(42) {} int v; }; int main() { std::atomic<S> as; // Default-initialization std::cout << as.load().v << std::endl; // ? }
For a user-defined emulation of atomic
the expected outcome would be defined and the program would output "42",
but existing implementations differ and the result value is a "random number" for at least one implementation. This seems
very surprising to me.
S
.
According to my understanding, the non-normative note in 99 [atomics.types.operations.req] p4 is intended to
refer to types that are valid C
-types, but the example type S
is not such a type.
To make the mental model of atomic
's default constructor more intuitive for user-code, I suggest to clarify the wording
to have the effects of default-initialization instead. The current state seems more like an unintended effect of imprecise
language used here and has some similarities to wording that was incorrectly used to specify atomic_flag
initialization
as described by LWG 2159(i).
[2014-05-17, Daniel comments and provides alternative wording]
The current wording was considered controversial as expressed by reflector discussions. To me, the actual problem is not newly introduced by that wording, but instead is already present in basically all paragraphs specifying semantics of atomic types, since the wording never clearly distinguishes the value of the actual atomic type A and the value of the "underlying", corresponding non-atomic type C. The revised proposed wording attempts to improve the current ambiguity of these two kinds of values.
Previous resolution from Daniel [SUPERSEDED]:
This wording is relative to N3691.
Modify 99 [atomics.types.operations.req] p4 as indicated: [Editorial note: There is no exposition-only member in
atomic
, which makes it a bit hard to specify what actually is initialized, but the usage of the term "value" seems consistent with similar wording used to specify the effects of the atomicload
functions]A ::A () noexcept = default;-4- Effects:
leaves the atomic object in an uninitialized stateThe value of the atomic object is default-initialized (9.4 [dcl.init]). [Note: These semantics ensure compatibility withC
. — end note]
[2015-02 Cologne]
Handed over to SG1.
[2017-07 Toronto]
SG1 reviewed the PR below:
Previous resolution [SUPERSEDED]:This wording is relative to N3936.
Modify 99 [atomics.types.operations.req] p2 as indicated: [Editorial note: This is a near-to editorial change not directly affecting this issue, but
atomic_address
does no longer exist and the pointed to definition is relevant in the context of this issue resolution.]-2- In the following operation definitions:
an A refers to one of the atomic types.
a C refers to its corresponding non-atomic type.
Theatomic_address
atomic type corresponds to thevoid*
non-atomic type.[…]
Modify 99 [atomics.types.operations.req] p4 and the following as indicated: [Editorial note: There is no exposition-only member in
atomic
, which makes it a bit hard to specify what actually is initialized, but the introductory wording of 99 [atomics.types.operations.req] p2 b2 defines: "a C refers to its corresponding non-atomic type." which helps to specify the semantics in terms of "the C value referred to by the atomic object"]A::A() noexcept = default;-4- Effects:
leaves the atomic object in an uninitialized stateDefault-initializes (9.4 [dcl.init]) the C value referred to by the atomic object. [Note: These semantics ensure compatibility withC
. — end note]constexpr A::A(C desired) noexcept;-5- Effects: Direct-i
[…]Initializes the C value referred to by the atomic object with the valuedesired
. Initialization is not an atomic operation (1.10). […]void atomic_init(volatile A* object, C desired) noexcept; void atomic_init(A* object, C desired) noexcept;-8- Effects: Non-atomically initializes the C value referred to by
*object
with valuedesired
. […]void atomic_store(volatile A* object, C desired) noexcept; […] void A::store(C desired, memory_order order = memory_order_seq_cst) noexcept;-9- […]
-10- Effects: Atomically replaces the C value pointed to byobject
or bythis
with the value ofdesired
. […] […]C atomic_load(const volatile A* object) noexcept; […] C A::load(memory_order order = memory_order_seq_cst) const noexcept;-13- […]
-14- […] -15- Returns: Atomically returns the C value pointed to byobject
or bythis
. […]C atomic_exchange(volatile A* object, C desired) noexcept; […] C A::exchange(C desired, memory_order order = memory_order_seq_cst) noexcept;-18- Effects: Atomically replaces the C value pointed to by
-19- Returns: Atomically returns the C value pointed to byobject
or bythis
withdesired
. […]object
or bythis
immediately before the effects. […]C atomic_fetch_key(volatile A* object, M operand) noexcept; […] C A::fetch_key(M operand, memory_order order = memory_order_seq_cst) noexcept;-28- Effects: Atomically replaces the C value pointed to by
-29- Returns: Atomicallyobject
or bythis
with the result of the computation applied to the C value pointed to byobject
or bythis
and the givenoperand
. […],returns the C value pointed to byobject
or bythis
immediately before the effects. […]Modify 33.5.10 [atomics.flag] p5 and the following as indicated:
bool atomic_flag_test_and_set(volatile atomic_flag* object) noexcept; […] bool atomic_flag::test_and_set(memory_order order = memory_order_seq_cst) noexcept;-5- Effects: Atomically sets the bool value pointed to by
-6- Returns: Atomicallyobject
or bythis
totrue
. […],returns the bool valueof thepointed to byobject
or bythis
immediately before the effects.void atomic_flag_clear(volatile atomic_flag* object) noexcept; […] void atomic_flag::clear(memory_order order = memory_order_seq_cst) noexcept;-7- […]
-8- Effects: Atomically sets the bool value pointed to byobject
or bythis
tofalse
. […]
SG1 also reviewed another PR from Lawrence Crowl. Lawrence's feedback was that turning atomic<T> into a container of T was a mistake, even if we allow the implementation of atomic to contain a T. SG1 agreed with Lawrence, but his PR (http://wiki.edg.com/bin/view/Wg21toronto2017/DefaultInitNonContainer) had massive merge conflicts caused by the adoption of P0558. Billy O'Neal supplied a new PR, which SG1 agreed to and which LWG looked at informally. This change also makes it clearer that initialization of an atomic is not an atomic operation in all forms, changes the C compatibility example to actually be compatible with C, and removes "initialization-compatible" which is not defined anywhere.
SG1 considered moving ATOMIC_VAR_INIT
into Annex D, as their understanding at this time is that WG14 is considering removal of that macro. However, consensus was that moving things between clauses would require a paper, and that we should wait to remove that until WG14 actually does so.
[2019-02, Monday in Kona]
While discussing Richard's P1286 paper, we noted that this issue's resolution needs to be updated based on that discussion.
Also, the idea that atomic() noexcept = default
is ok will not fly for implementors who store additional information inside the atomic variable.
Previous resolution [SUPERSEDED]:
This wording is relative to N4659.
Modify 33.5.8.2 [atomics.types.operations] as indicated:
-?- Initialization of an atomic object is not an atomic operation (6.9.2 [intro.multithread]). [Note: It is possible to have an access to an atomic object A race with its construction, for example by communicating the address of the just-constructed object A via a
memory_order_relaxed
operations on a suitable atomic pointer variable, and then immediately accessing A in the recieving thread. This results in undefined behavior. — end note]-1- [Note: Many operations are volatile-qualified. The "volatile as device register" semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects. It does not mean that operations on non-volatile objects become volatile. — end note]
atomic() noexcept = default;-2- Effects:
Leaves the atomic object in an uninitialized state. [Note: These semantics ensure compatibility with C. — end note]Initializes the atomic object with a default-initialized (9.4 [dcl.init]) value of typeT
. [Note: The default-initialized value may not be pointer-interconvertible with the atomic object. — end note]constexpr atomic(T desired) noexcept;-3- Effects: Initializes the atomic object with the value
desired
.Initialization is not an atomic operation (6.9.2 [intro.multithread]). [Note: It is possible to have an access to an atomic object A race with its construction, for example by communicating the address of the just-constructed object A to another thread viamemory_order_relaxed
operations on a suitable atomic pointer variable, and then immediately accessing A in the receiving thread. This results in undefined behavior — end note]#define ATOMIC_VAR_INIT(value)see below{value}-4-
The macro expands to a token sequence suitable for constant initialization of an atomic variable of static storage duration of a type that is initialization-compatible with value. [Note: This operation may need to initialize locks. — end note] Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race.[Note: This macro ensures compatibility with C. — end note]
[Example:
atomic<int>atomic_int v = ATOMIC_VAR_INIT(5);
— end example]
[2019-11; Resolved by the adoption of P0883 in Belfast.]
Proposed resolution:
Resolved by P0883.