This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-11-11


1953. Data races and common initial sequence

Section: 6.7.1  [intro.memory]     Status: tentatively ready     Submitter: Faisal Vali     Date: 2014-06-23

According to 6.7.1 [intro.memory] paragraph 3,

A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having non-zero width. [Note: Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation. —end note] Two or more threads of execution (6.9.2 [intro.multithread]) can update and access separate memory locations without interfering with each other.

It is not clear how this relates to the permission granted in 11.4 [class.mem] paragraph 18 to inspect the common initial sequence of standard-layout structs that are members of a standard-layout union. If one thread is writing to the common initial sequence and another is reading from it via a different struct, that should constitute a data race, but the current wording does not clearly state that.

Additional notes (October, 2024)

(From submission #621.)

A similar concern arises for the following example:

  union U { int x, y; } u;
  (u.x = 1, 0) + (u.y = 2, 0);

Possible resolution [SUPERSEDED]:

Change in 6.7.1 [intro.memory] paragraph 3 as follows:

A memory location is the storage occupied by the object representation of either an object of scalar type that is not a bit-field or a maximal sequence of adjacent bit-fields all having nonzero width. ...

CWG 2024-10-25

Subclause 6.9.1 [intro.execution] paragraph 10 does not cover unsequenced object creation that does not change any bits of storage, such as a placement new invoking a trivial default constructor. The original concern in this issue was addressed by P0137R1, adding the following in 11.4.2 [class.mfct] paragraph 28:

In a standard-layout union with an active member (11.5 [class.union]) of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2; the behavior is as if the corresponding member of T1 were nominated.

Proposed resolution (approved by CWG 2024-11-08):

  1. Change in 6.7.1 [intro.memory] paragraph 3 as follows:

    A memory location is the storage occupied by the object representation of either an object of scalar type that is not a bit-field or a maximal sequence of adjacent bit-fields all having nonzero width. ...
  2. Change in 6.9.1 [intro.execution] paragraph 10 and add bullets as follows:

    ... The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If The behavior is undefined if
    • a side effect on a memory location (6.7.1 [intro.memory]) or
    • starting or ending the lifetime of an object in a memory location
    is unsequenced relative to either
    • another side effect on the same memory location,
    • starting or ending the lifetime of an object occupying storage that overlaps with the memory location, or
    • a value computation using the value of any object in the same memory location,
    and they the two evaluations are not potentially concurrent (6.9.2 [intro.multithread]) , the behavior is undefined. [ Note: Starting the lifetime of an object in a memory location can end the lifetime of objects in other memory locations (6.7.3 [basic.life]). -- end note ]

    [ Note: ... ]

    [ Example:

      void g(int i) {
        i = 7, i++, i++;     // i becomes 9
        i = i++ + 1;   // the value of i is incremented
        i = i++ + i;   // undefined behavior
        i = i + 1;     // the value of i is incremented
        union U { int x, y; } u;
        (u.x = 1, 0) + (u.y = 2, 0);   // undefined behavior
      }
    

    -- end example ]

  3. Change in 6.9.2.2 [intro.races] paragraph 2 as follows, adding bullets:

    Two expression evaluations conflict if one of them
    • modifies (3.1 [defns.access]) a memory location (6.7.1 [intro.memory]) or
    • starts or ends the lifetime of an object in a memory location
    and the other one
    • reads or modifies the same memory location or
    • starts or ends the lifetime of an object occupying storage that overlaps with the memory location.
    [Note 2: A modification can still conflict even if it does not alter the value of any bits. —end note]