ISO/IEC JTC1 SC22 WG14 N1482 - 2010-05-26
Paul E. McKenney, [email protected]
Mark Batty, [email protected]
Clark Nelson, [email protected]
N.M. Maclaren, [email protected]
Hans Boehm, [email protected]
Anthony Williams, [email protected]
Peter Dimov, [email protected]
Lawrence Crowl, [email protected], [email protected]
Mark Batty recently undertook a partial formalization of the C++ memory model, which Mark summarized in N2955. This paper summarizes the discussions on Mark's paper, both verbal and email, recommending appropriate actions for the Library Working Group. Core issues are dealt with in a companion N3074 paper.
This paper is based on N1445, which is in turn based on WG21 N3045, and has been updated to reflect discussions in the Concurrency subgroup of the Library Working Group in Pittsburgh. This paper also carries the C-language side of N3040, which was also discussed in the Concurrency subgroup of the Library Working Group in Pittsburgh.
Add a note stating that memory_order_relaxed
operations
must maintain indivisibility, as described in the discussion of 1.10p4.
This must be considered in conjunction with the resolution to LWG 1151,
which is expected to be addressed by Hans Boehm in N3040.
The second sentence of this paragraph, “Implementations shall not move an atomic operation out of an unbounded loop”, does not add anything to the first sentence, and, worse, can be interpreted as restricting the meaning of the first sentence. This sentence should therefore be deleted. The Library Working Group discussed this change during the Santa Cruz meeting in October 2009, and agreed with this deletion.
This topic was the subject of a spirited discussion among a subset of the participants in the C/C++-compatibility effort this past October and November.
Unlike C++, C has no mechanism to force a given variable to be initialized. Therefore, if C++ atomics are going to be compatible with those of C, either C++ needs to tolerate uninitialized atomic objects, or C needs to require that all atomic objects be initialized. There are a number of cases to consider:
={value}
” syntax may be used
to explicitly initialize these values, however, such initialization
may not contain any statements executing at run time.
auto
variables. The C standard does not
require that these be initialized.
On some machines, such variables might be
initialized to an error value (for example, not-a-thing (NAT)
for variables on Itanium that live only in a machine register).
The C “={value}
” syntax may be used
to explicitly initialize these values, and may include
statements executing at run time.
malloc()
.
The C standard does not require that these be initialized.
The C “={value}
” syntax may not be
used to explicitly initialize these values.
Of course, C on-stack auto
variables and dynamically
allocated variables are inaccessible to other threads until references
to them are published.
Such publication must ensure that any initialization happens before any
access to the variable from another thread, for example, by use of
store release or locking.
There are also a number of interesting constraints on these types:
These constraints permit but three known ways for C++ to make use of non-generic atomic types defined in C-language translation units:
auto
variables.
The wording below permits any of the above implementation alternatives. Note that WG14's C-language working draft also requires initializers for non-flag atomic types (initialization is already provided in the C++ working draft via constructors). These are listed in a subsequent section.
The current WG14 wording assumes that all integral types will have
the same degree of lock freedom.
This assumption fails for long long
on 32-bit machines,
and would fail for other types for machines with smaller word sizes.
WG14 therefore needs separate per-integral-type macros that indicate
whether the corresponding type has a lock-free implementation.
As noted in N3040, the current specification is not very clear on what it means for an “atomic read-modify-write operation” to be “atomic”. In particular, a given atomic read-modify-write operations is required to read the last value written before the write associated with that read-modify-write operation.
Add a note after 2.93p1 as follows:
The enumeration
memory_order
specifies the detailed regular (non–atomic) memory synchronization order as defined in 1.10 and may provide for operation ordering. Its enumerated values and their meanings are as follows:— memory_order_relaxed: no operation orders memory.
—memory_order_release
,memory_order_acq_rel
, andmemory_order_seq_cst
: a store operation performs a release operation on the affected memory location.
—memory_order_consume
: a load operation performs a consume operation on the affected memory location.
—memory_order_acquire
,memory_order_acq_rel
, andmemory_order_seq_cst
: a load operation performs an acquire operation on the affected memory location.[ Note: Atomic operations specifying
memory_order_relaxed
are relaxed only with respect to memory ordering. Implementations must still guarantee that any given atomic access to a particular atomic object be indivisible with respect to all other atomic accesses to that object. — end note. ]
Therefore, remove the second sentence of 29.3p11 as follows:
Implementations should make atomic stores visible to atomic loads within a reasonable amount of time.
Implementations shall not move an atomic operation out of an unbounded loop.
Add the following to WG21 29.5.1 (Integral Types) in locations
corresponding to the existing atomic_is_lock_free()
functions:
void atomic_init(volatile atomic_bool*, bool);
void atomic_init(atomic_bool*, bool);
void atomic_init(volatile atomic_itype*, itype);
void atomic_init(atomic_itype*, itype);
Note that ATOMIC_INIT
is already in use, for example, in
the Linux kernel.
Google code search was unable to find ATOMIC_VAR_INIT
or
atomic_init
.
Add the following to WG21 29.5.2 (Address Type) located
corresponding to the existing atomic_is_lock_free()
function:
void atomic_init(volatile atomic_address*, void *);
void atomic_init(atomic_address*, void *);
Add the following after WG21 29.6p4 (Operations on Atomic Types):
#define ATOMIC_VAR_INIT(value)
see belowRemarks: A macro expanding to a token sequence suitable for initializing an atomic variable of a type that is initialization–compatible with value. Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race.
[ Example:
atomic_int v = ATOMIC_VAR_INIT(5);
— end example ]
Add the following after WG21 29.6p5 (Operations on Atomic Types):
void atomic_init(volatile A *object, C desired);
void atomic_init(A *object, C desired);
Effects: Non–atomically assigns the value desired to object. Concurrent access from another thread, even via an atomic operation, constitutes a data race.
Change WG14 7.16.1p1 as follows:
The header <stdatomic.h> defines
threeseveral macros and declares several types and functions for performing atomic operations on data shared between threads.
Change WG14 7.16.1p2 as follows:
The macros defined are
ATOMIC_INTEGRAL_LOCK_FREE
ATOMIC_CHAR_LOCK_FREE
ATOMIC_CHAR16_T_LOCK_FREE
ATOMIC_CHAR32_T_LOCK_FREE
ATOMIC_WCHAR_T_LOCK_FREE
ATOMIC_SHORT_LOCK_FREE
ATOMIC_INT_LOCK_FREE
ATOMIC_LONG_LOCK_FREE
ATOMIC_LLONG_LOCK_FREE
ATOMIC_ADDRESS_LOCK_FREE
which indicate the
generallock–free property ofinteger and addressthe corresponding atomic types, with the signed and unsigned variants grouped together; and
ATOMIC_FLAG_INIT
which expands to an initializer for an object of type
atomic_flag
.
As called out in N3040 “More precise definition of atomic”, add a paragraph to WG14 following 7.16.2p9 as follows:
Atomic read-modify-write operations shall always read the last value (in the modification order) written before the write associated with the read-modify-write operation.
Add the following note after 7.16.2p6:
NOTE: Atomic operations specifying
memory_order_relaxed
are relaxed only with respect to memory ordering. Implementations must still guarantee that any given atomic access to a particular atomic object be indivisible with respect to all other atomic accesses to that object.
Change WG14 7.16.2p13 as follows:
Implementations should make atomic stores visible to atomic loads within a reasonable amount of time.
Implementations shall not move an atomic operation out of an unbounded loop.
Add a new section to WG14 named “Initialization”:
7.16.N Initialization
7.16.N.1 The
ATOMIC_VAR_INIT
macro
#include <stdatomic.h>
#define ATOMIC_VAR_INIT(C value)
The macro
ATOMIC_VAR_INIT
expands to a token sequence suitable for initializing an atomic variable of a type that is initialization-compatible withvalue
. Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race. However, the default zero–initialization is guaranteed to produce a valid object where it applies.EXAMPLE
atomic_int guide = ATOMIC_VAR_INIT(42);
A non-static atomic variable that is not explicitly initialized with
ATOMIC_VAR_INIT
is initially in an indeterminate state.
Add a new section to WG14 named “7.16.N.2 The atomic_init
generic function”:
Synopsis
#include <stdatomic.h>
void atomic_init(volatile A *obj, C value);
Description
Initializes the atomic object
obj
to the valuevalue
, while also initializing any additional state that the implementation might need to carry for the atomic object. Although this function initializes an atomic object, it does not avoid data races.EXAMPLE
atomic_init(&p->a, 42);