Adjusting C++ Atomics for C Compatibility

ISO/IEC JTC1 SC22 WG21 N3193 = 10-0183 - 2010-11-12

Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org

This paper revises ISO/IEC JTC1 SC22 WG21 N3164 = 10-0154 - 2010-10-14.

Introduction
Wording
    29.2 Header <atomic> synopsis [atomics.syn]
    29.5 Atomic Types [atomics.types]
    29.5.1 Integral Types [atomics.types.integral]
    29.5.2 Address Type [atomics.types.address]
    29.5.3 Generic Type [atomics.types.generic]
    29.6 Operations on Atomic Types [atomics.types.operations]

Introduction

The draft of C1X includes a facility for atomics. The primary change in this facility in the draft N1494 is incorporation of a new atomics proposal for a productive syntax for declaring atomic types and operators for atomic types. (The latest C1X draft is N1516.) C++0x FCD national body comments CA 23, CH 22, GB 128 and more generally US 1 request compatibility between C and C++ with respect to atomics.

This paper provides normative wording changes to reflect the choices in N3137 C and C++ Liaison: Compatibility for Atomics.

In summary, the changes are:

Other issues addressed by this paper are as follows:

GB 132 addressed by refactoring of definitions
GB 133, US 161, US 163, US 164 made moot by the removal of atomic_address

Wording

This wording is relative to N3126 Working Draft, Standard for Programming Language C++.

29.2 Header <atomic> synopsis [atomics.syn]

Remove the ATOMIC_ADDRESS_LOCK_FREE macro.

Move the ATOMIC_VAR_INIT macro declaration to its proper order with respect to the sections.

Move the atomic_flag type and function declarations to their proper order with respect to the sections.

Remove the declaration block for atomic_bool and associated functions.

Remove the declaration block for atomic_itype and associated functions.

Remove the declaration block for atomic_address and associated functions.

Modify the section number for the generic type definitions to 29.5.

For section 29.6, add generic free functions as follows. The functions mirror C type-generic macros.

Within the following declarations, atomic-type is either atomic<Type> or a named base class for Type from table AA or implied from table BB.


template <typename Type>
bool atomic_is_lock_free(const volatile atomic-type*);
template <typename Type>
bool atomic_is_lock_free(const atomic-type*);
template <typename Type>
void atomic_init(volatile atomic-type*, Type);
template <typename Type>
void atomic_init(atomic-type*, Type);
template <typename Type>
void atomic_store(volatile atomic-type*, Type);
template <typename Type>
void atomic_store(atomic-type*, Type);
template <typename Type>
void atomic_store_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
void atomic_store_explicit(atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_load(const volatile atomic-type*);
template <typename Type>
Type atomic_load(const atomic-type*);
template <typename Type>
Type atomic_load_explicit(const volatile atomic-type*, memory_order);
template <typename Type>
Type atomic_load_explicit(const atomic-type*, memory_order);
template <typename Type>
Type atomic_exchange(volatile atomic-type*, Type);
template <typename Type>
Type atomic_exchange(atomic-type*, Type);
template <typename Type>
Type atomic_exchange_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_exchange_explicit(atomic-type*, Type, memory_order);
template <typename Type>
bool atomic_compare_exchange_weak(volatile atomic-type*, Type*, Type);
template <typename Type>
bool atomic_compare_exchange_weak(atomic-type*, Type*, Type);
template <typename Type>
bool atomic_compare_exchange_strong(volatile atomic-type*, Type*, Type);
template <typename Type>
bool atomic_compare_exchange_strong(atomic-type*, Type*, Type);
template <typename Type>
bool atomic_compare_exchange_weak_explicit(volatile atomic-type*, Type*,
                                           Type, memory_order, memory_order);
template <typename Type>
bool atomic_compare_exchange_weak_explicit(atomic-type*, Type*,
                                           Type, memory_order, memory_order);
template <typename Type>
bool atomic_compare_exchange_strong_explicit(volatile atomic-type*, Type*,
                                             Type, memory_order, memory_order);
template <typename Type>
bool atomic_compare_exchange_strong_explicit(atomic-type*, Type*,
                                             Type, memory_order, memory_order);

For section 29.6, add generic free functions as follows.

Each of the following declarations has no implementation. In each declaration, atomic-type is either atomic<Type> or a named base class for Type from table AA or implied from table BB.


template <typename Type>
Type atomic_fetch_add(volatile atomic-type*, Type);
template <typename Type>
Type atomic_fetch_add(atomic-type*, Type);
template <typename Type>
Type atomic_fetch_add_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_add_explicit(atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_sub(volatile atomic-type*, Type);
template <typename Type>
Type atomic_fetch_sub(atomic-type*, Type);
template <typename Type>
Type atomic_fetch_sub_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_sub_explicit(atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_and(volatile atomic-type*, Type);
template <typename Type>
Type atomic_fetch_and(atomic-type*, Type);
template <typename Type>
Type atomic_fetch_and_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_and_explicit(atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_or(volatile atomic-type*, Type);
template <typename Type>
Type atomic_fetch_or(volatile atomic-type*, Type);
template <typename Type>
Type atomic_fetch_or_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_or_explicit(atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_xor(volatile atomic-type*, Type);
template <typename Type>
Type atomic_fetch_xor(atomic-type*, Type);
template <typename Type>
Type atomic_fetch_xor_explicit(volatile atomic-type*, Type, memory_order);
template <typename Type>
Type atomic_fetch_xor_explicit(atomic-type*, Type, memory_order);

For section 29.6, add the following integral specializations.

In each of the following declarations, integral is an integral type, atomic-integral is either atomic<integral> or a named base class for integral from table AA or implied from table BB.


template <>
integral atomic_fetch_add(volatile atomic-integral*, integral);
template <>
integral atomic_fetch_add(atomic-integral*, integral);
template <>
integral atomic_fetch_add_explicit(volatile atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_add_explicit(atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_sub(volatile atomic-integral*, integral);
template <>
integral atomic_fetch_sub(atomic-integral*, integral);
template <>
integral atomic_fetch_sub_explicit(volatile atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_sub_explicit(atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_and(volatile atomic-integral*, integral);
template <>
integral atomic_fetch_and(atomic-integral*, integral);
template <>
integral atomic_fetch_and_explicit(volatile atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_and_explicit(atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_or(volatile atomic-integral*, integral);
template <>
integral atomic_fetch_or(volatile atomic-integral*, integral);
template <>
integral atomic_fetch_or_explicit(volatile atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_or_explicit(atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_xor(volatile atomic-integral*, integral);
template <>
integral atomic_fetch_xor(atomic-integral*, integral);
template <>
integral atomic_fetch_xor_explicit(volatile atomic-integral*, integral,
                                   memory_order);
template <>
integral atomic_fetch_xor_explicit(atomic-integral*, integral,
                                   memory_order);

For section 29.6, add generic free functions as follows. These functions correspond to the atomic class template partial specialization for pointers.


template <typename Type>
Type atomic_fetch_add(volatile atomic<Type*>*, ptrdiff_t);
template <typename Type>
Type atomic_fetch_add(atomic<Type*>*, ptrdiff_t);
template <typename Type>
Type atomic_fetch_add_explicit(volatile atomic<Type*>*, ptrdiff_t,
                               memory_order);
template <typename Type>
Type atomic_fetch_add_explicit(atomic<Type*>*, ptrdiff_t,
                               memory_order);
template <typename Type>
Type atomic_fetch_sub(volatile atomic<Type*>*, ptrdiff_t);
template <typename Type>
Type atomic_fetch_sub(atomic<Type*>*, ptrdiff_t);
template <typename Type>
Type atomic_fetch_sub_explicit(volatile atomic<Type*>*, ptrdiff_t,
                               memory_order);
template <typename Type>
Type atomic_fetch_sub_explicit(atomic<Type*>*, ptrdiff_t,
                               memory_order);

29.5 Atomic Types [atomics.types]

This section has the bulk of changes, removing the named types in favor of only providing the productive type syntax. The approach is to remove the sections on integral types and address types, and turn the subsection on generic types to the section on types.

29.5.1 Integral Types [atomics.types.integral]

Remove this section.

29.5.2 Address Type [atomics.types.address]

Remove this section.

29.5.3 Generic Type [atomics.types.generic]

Rename this subsection to 29.5 Atomic Types [atomics.types].

Edit the synopsis for the integral type specializations as follows.


template <> struct atomic<integral> : atomic_itype {

    bool is_lock_free() const volatile;
    bool is_lock_free() const;
    void store(integral, memory_order = memory_order_seq_cst) volatile;
    void store(integral, memory_order = memory_order_seq_cst);
    integral load(memory_order = memory_order_seq_cst) const volatile;
    integral load(memory_order = memory_order_seq_cst) const;
    operator integral() const volatile;
    operator integral() const;
    integral exchange(integral,
                      memory_order = memory_order_seq_cst) volatile;
    integral exchange(integral,
                      memory_order = memory_order_seq_cst);
    bool compare_exchange_weak(integral&, integral,
                               memory_order, memory_order) volatile;
    bool compare_exchange_weak(integral&, integral,
                               memory_order, memory_order);
    bool compare_exchange_strong(integral&, integral,
                                 memory_order, memory_order) volatile;
    bool compare_exchange_strong(integral&, integral,
                                 memory_order, memory_order);
    bool compare_exchange_weak(integral&, integral,
                               memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_weak(integral&, integral,
                               memory_order = memory_order_seq_cst);
    bool compare_exchange_strong(integral&, integral,
                                 memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_strong(integral&, integral,
                                 memory_order = memory_order_seq_cst);
    integral fetch_add(integral,
                       memory_order = memory_order_seq_cst) volatile;
    integral fetch_add(integral,
                       memory_order = memory_order_seq_cst);
    integral fetch_sub(integral,
                       memory_order = memory_order_seq_cst) volatile;
    integral fetch_sub(integral,
                       memory_order = memory_order_seq_cst);
    integral fetch_and(integral,
                       memory_order = memory_order_seq_cst) volatile;
    integral fetch_and(integral,
                       memory_order = memory_order_seq_cst);
    integral fetch_or(integral,
                       memory_order = memory_order_seq_cst) volatile;
    integral fetch_or(integral,
                       memory_order = memory_order_seq_cst);
    integral fetch_xor(integral,
                       memory_order = memory_order_seq_cst) volatile;
    integral fetch_xor(integral,
                       memory_order = memory_order_seq_cst);

    atomic() = default;
    constexpr atomic(integral);
    atomic(const atomic&) = delete;
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
    integral operator=(integral) volatile;
    integral operator=(integral);

    integral operator++(int) volatile;
    integral operator++(int);
    integral operator--(int) volatile;
    integral operator--(int);
    integral operator++() volatile;
    integral operator++();
    integral operator--() volatile;
    integral operator--();
    integral operator+=(integral) volatile;
    integral operator+=(integral);
    integral operator-=(integral) volatile;
    integral operator-=(integral);
    integral operator&=(integral) volatile;
    integral operator&=(integral);
    integral operator|=(integral) volatile;
    integral operator|=(integral);
    integral operator^=(integral) volatile;
    integral operator^=(integral);
};

Edit the synopsis for the pointer type specializations as follows.


template <class T> struct atomic<T*> : atomic_address {

    bool is_lock_free() const volatile;
    bool is_lock_free() const;
    void store(T*, memory_order = memory_order_seq_cst) volatile;
    void store(T*, memory_order = memory_order_seq_cst);
    T* load(memory_order = memory_order_seq_cst) const volatile;
    T* load(memory_order = memory_order_seq_cst) const;
    operator T*() const volatile;
    operator T*() const;
    T* exchange(T*, memory_order = memory_order_seq_cst) volatile;
    T* exchange(T*, memory_order = memory_order_seq_cst);
    bool compare_exchange_weak(T*&, T*,
                               memory_order, memory_order) volatile;
    bool compare_exchange_weak(T*&, T*,
                               memory_order, memory_order);
    bool compare_exchange_strong(T*&, T*,
                                 memory_order, memory_order) volatile;
    bool compare_exchange_strong(T*&, T*,
                                 memory_order, memory_order);
    bool compare_exchange_weak(T*&, T*,
                               memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_weak(T*&, T*,
                               memory_order = memory_order_seq_cst);
    bool compare_exchange_strong(T*&, T*,
                                 memory_order = memory_order_seq_cst) volatile;
    bool compare_exchange_strong(T*&, T*,
                                 memory_order = memory_order_seq_cst);
    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) volatile;
    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst);
    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) volatile;
    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst);
    
    atomic() = default;
    constexpr atomic(T*);
    atomic(const atomic&) = delete;
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;

    T* operator=(T*) volatile;
    T* operator=(T*);
    T* operator++(int) volatile;
    T* operator++(int);
    T* operator--(int) volatile;
    T* operator--(int);
    T* operator++() volatile;
    T* operator++();
    T* operator--() volatile;
    T* operator--();
    T* operator+=(ptrdiff_t) volatile;
    T* operator+=(ptrdiff_t);
    T* operator-=(ptrdiff_t) volatile;
    T* operator-=(ptrdiff_t);
};

After paragraph 1, add a new paragraph.

The semantics of the operations on atomic specializations are defined in 29.6.

Edit paragraph 3 as follows.

There are full specializations over the integral types (char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t, and any other types need by <cstdint> typedefs.) on the atomic class template. For each integral type integral in the second column of Table 142 or Table 143, the specialization atomic<integral> shall be publicly derived from the corresponding atomic integral type in the first column of the table. In addition, the specialization atomic<bool> shall be publicly derived from atomic_bool. provide additional atomic operations appropriate to integral types. These specializations shall have trivial default constructors and trivial destructors. The atomic integral specializations shall have standard layout. They shall each have a trivial default constructor, and a trivial destructor. They shall each support aggregate initialization syntax.

Insert a new paragraph after the one above.

The specialization atomic<bool> shall have standard layout. It shall have a trivial default constructor and a trivial destructor. It shall support aggregate initialization syntax.

Edit paragraph 4 as follows.

There are pointer partial specializations on the atomic class template. These specializations shall be publicly derived from atomic_address. The unit of addition/subtraction for these specializations shall be the size of the referenced type. These specializations shall have trivial default constructors and trivial destructors.

Insert a new paragraph after the one above.

[Note: The representation of atomic specializations need not have the same size as their corresponding argument types. They should have the same size whenever possible, as it eases effort required to port existing code. —end note]

After paragraph 4, add a new paragraph and tables as follows.

There are named types corresponding to the integral specializations of atomic, as specified in table AA. These named types are either typedefs to the corresponding specialization, or base classes of those specializations. If they are bases classes, those classes shall support the same member functions as their specialization.

Table AAatomic integral typedefs
Named TypeIntegral Argument Type
atomic_charchar
atomic_scharsigned char
atomic_ucharunsigned char
atomic_shortshort
atomic_ushortunsigned short
atomic_intint
atomic_uintunsigned int
atomic_longlong
atomic_ulongunsigned long
atomic_llonglong long
atomic_ullongunsigned long long
atomic_char16_tchar16_t
atomic_char32_tchar32_t
atomic_wchar_twchar_t

There are atomic typedefs corresponding to the typedefs in <inttypes.h> as specified in table BB.

Table BBatomic <inttypes.h> typedefs
Atomic Typedefinttypes.h Type
atomic_int_least8_tint_least8_t
atomic_uint_least8_tuint_least8_t
atomic_int_least16_tint_least16_t
atomic_uint_least16_tuint_least16_t
atomic_int_least32_tint_least32_t
atomic_uint_least32_tuint_least32_t
atomic_int_least64_tint_least64_t
atomic_uint_least64_tuint_least64_t
atomic_int_fast8_tint_fast8_t
atomic_uint_fast8_tuint_fast8_t
atomic_int_fast16_tint_fast16_t
atomic_uint_fast16_tuint_fast16_t
atomic_int_fast32_tint_fast32_t
atomic_uint_fast32_tuint_fast32_t
atomic_int_fast64_tint_fast64_t
atomic_uint_fast64_tuint_fast64_t
atomic_intptr_tintptr_t
atomic_uintptr_tuintptr_t
atomic_size_tsize_t
atomic_ptrdiff_tptrdiff_t
atomic_intmax_tintmax_t
atomic_uintmax_tuintmax_t

29.6 Operations on Atomic Types [atomics.types.operations]

The free functions in this section become templates or specializations thereof. Technically, this would require adding a template name signature. However, that change seems likely to be more confusing than helpful. So, I have not suggested that change, leaving it to the editor for the final decision.

Edit paragraph 1 as follows.

There are only a few kinds of operations on atomic types, though there are many instances on those kinds. This section specifies each general kind. The specific instances are defined in 29.2 29.5.1, 29.5.2, and 29.5.3 29.5.

Edit paragraph 5 as follows.

Remarks: A macro that expands to a token sequence suitable for initializing constant initialization of an atomic variable of static storage duration of a type that is initializion-compatible 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. [Example:


atomic_int atomic<int> v = ATOMIC_VAR_INIT(5);

end example]

Edit paragraph paragraph 7 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.