1478. Clarify race conditions in atomics initialization

Section: 32.6.1 [atomics.types.operations] Status: C++11 Submitter: BSI Opened: 2010-08-25 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [atomics.types.operations].

View all issues with C++11 status.

Discussion:

Addresses GB-136

GB requests normative clarification in 32.6.1 [atomics.types.operations] p.4 that concurrent access constitutes a race, as already done on p.6 and p.7.

[ Resolution proposed in ballot comment: ]

Initialisation of atomics:

We believe the intent is that for any atomics there is a distinguished initialisation write, but that this need not happens-before all the other operations on that atomic - specifically so that the initialisation write might be non-atomic and hence give rise to a data race, and hence undefined behaviour, in examples such as this (from Hans):

atomic<atomic<int> *> p
f()                      |
{ atomic<int>x;          | W_na x
  p.store(&x,mo_rlx);    | W_rlx p=&x
}                        |

(where na is nonatomic and rlx is relaxed). We suspect also that no other mixed atomic/nonatomic access to the same location is intended to be permitted. Either way, a note would probably help.

[2011-02-26: Hans comments and drafts wording]

I think the important point here is to clarify that races on atomics are possible, and can be introduced as a result of non-atomic initialization operations. There are other parts of this that remain unclear to me, such as whether there are other ways to introduce data races on atomics, or whether the races with initialization also introduce undefined behavior by the 3.8 lifetime rules. But I don't think that it is necessary to resolve those issues before releasing the standard. That's particularly true since we've introduced atomic_init, which allows easier ways to construct initialization races.

[2011-03 Madrid]

Accepted to be applied immediately to the WP

Proposed resolution:

  1. Update 99 [atomics.types.operations.req] p. 5 as follows:

    constexpr A::A(C desired);
    

    5 Effects: Initializes the object with the value desired. [ Note: Construction is not atomic. — end note ] Initialization is not an atomic operation (1.10) [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 via memory_order_relaxed atomic operations on a suitable atomic pointer variable, and then immediately accessing A in the receiving thread. This results in undefined behavior. — end note]

  2. In response to the editor comment to 99 [atomics.types.operations.req] p. 8: The first Effects element is the correct and intended one:

    void atomic_init(volatile A *object, C desired);
    void atomic_init(A *object, C desired);
    

    8 Effects: Non-atomically initializes *object with value desired. This function shall only be applied to objects that have been default constructed, and then only once. [ Note: these semantics ensure compatibility with C. — end note ] [ Note: Concurrent access from another thread, even via an atomic operation, constitutes a data race. — end note ] [Editor's note: The preceding text is from the WD as amended by N3196. N3193 makes different changes, marked up in the paper as follows:] Effects: Dynamically initializes an atomic variable. Non-atomically That is, non-atomically assigns the value desired to *object. [ Note: this operation may need to initialize locks. — end note ] Concurrent access from another thread, even via an atomic operation, constitutes a data race.