798 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			798 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
 | 
						|
====================
 | 
						|
``<atomic>`` Design
 | 
						|
====================
 | 
						|
 | 
						|
There were originally 3 designs under consideration. They differ in where most
 | 
						|
of the implementation work is done. The functionality exposed to the customer
 | 
						|
should be identical (and conforming) for all three designs.
 | 
						|
 | 
						|
 | 
						|
Design A: Minimal work for the library
 | 
						|
======================================
 | 
						|
The compiler supplies all of the intrinsics as described below. This list of
 | 
						|
intrinsics roughly parallels the requirements of the C and C++ atomics proposals.
 | 
						|
The C and C++ library implementations simply drop through to these intrinsics.
 | 
						|
Anything the platform does not support in hardware, the compiler
 | 
						|
arranges for a (compiler-rt) library call to be made which will do the job with
 | 
						|
a mutex, and in this case ignoring the memory ordering parameter (effectively
 | 
						|
implementing ``memory_order_seq_cst``).
 | 
						|
 | 
						|
Ultimate efficiency is preferred over run time error checking. Undefined
 | 
						|
behavior is acceptable when the inputs do not conform as defined below.
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    // In every intrinsic signature below, type* atomic_obj may be a pointer to a
 | 
						|
    // volatile-qualified type. Memory ordering values map to the following meanings:
 | 
						|
    //  memory_order_relaxed == 0
 | 
						|
    //  memory_order_consume == 1
 | 
						|
    //  memory_order_acquire == 2
 | 
						|
    //  memory_order_release == 3
 | 
						|
    //  memory_order_acq_rel == 4
 | 
						|
    //  memory_order_seq_cst == 5
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    // type represents a "type argument"
 | 
						|
    bool __atomic_is_lock_free(type);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    // Behavior is defined for mem_ord = 0, 1, 2, 5
 | 
						|
    type __atomic_load(const type* atomic_obj, int mem_ord);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    // Behavior is defined for mem_ord = 0, 3, 5
 | 
						|
    void __atomic_store(type* atomic_obj, type desired, int mem_ord);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    type __atomic_exchange(type* atomic_obj, type desired, int mem_ord);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    // Behavior is defined for mem_success = [0 ... 5],
 | 
						|
    //   mem_failure <= mem_success
 | 
						|
    //   mem_failure != 3
 | 
						|
    //   mem_failure != 4
 | 
						|
    bool __atomic_compare_exchange_strong(type* atomic_obj,
 | 
						|
                                        type* expected, type desired,
 | 
						|
                                        int mem_success, int mem_failure);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    // Behavior is defined for mem_success = [0 ... 5],
 | 
						|
    //   mem_failure <= mem_success
 | 
						|
    //   mem_failure != 3
 | 
						|
    //   mem_failure != 4
 | 
						|
    bool __atomic_compare_exchange_weak(type* atomic_obj,
 | 
						|
                                        type* expected, type desired,
 | 
						|
                                        int mem_success, int mem_failure);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    type __atomic_fetch_add(type* atomic_obj, type operand, int mem_ord);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    type __atomic_fetch_sub(type* atomic_obj, type operand, int mem_ord);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    type __atomic_fetch_and(type* atomic_obj, type operand, int mem_ord);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    type __atomic_fetch_or(type* atomic_obj, type operand, int mem_ord);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    type __atomic_fetch_xor(type* atomic_obj, type operand, int mem_ord);
 | 
						|
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    void* __atomic_fetch_add(void** atomic_obj, ptrdiff_t operand, int mem_ord);
 | 
						|
    void* __atomic_fetch_sub(void** atomic_obj, ptrdiff_t operand, int mem_ord);
 | 
						|
 | 
						|
    // Behavior is defined for mem_ord = [0 ... 5]
 | 
						|
    void __atomic_thread_fence(int mem_ord);
 | 
						|
    void __atomic_signal_fence(int mem_ord);
 | 
						|
 | 
						|
If desired the intrinsics taking a single ``mem_ord`` parameter can default
 | 
						|
this argument to 5.
 | 
						|
 | 
						|
If desired the intrinsics taking two ordering parameters can default ``mem_success``
 | 
						|
to 5, and ``mem_failure`` to ``translate_memory_order(mem_success)`` where
 | 
						|
``translate_memory_order(mem_success)`` is defined as:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    int translate_memory_order(int o) {
 | 
						|
        switch (o) {
 | 
						|
        case 4:
 | 
						|
            return 2;
 | 
						|
        case 3:
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        return o;
 | 
						|
    }
 | 
						|
 | 
						|
Below are representative C++ implementations of all of the operations. Their
 | 
						|
purpose is to document the desired semantics of each operation, assuming
 | 
						|
``memory_order_seq_cst``. This is essentially the code that will be called
 | 
						|
if the front end calls out to compiler-rt.
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_load(T const volatile* obj) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        return *obj;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    void __atomic_store(T volatile* obj, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        *obj = desr;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_exchange(T volatile* obj, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj = desr;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    bool __atomic_compare_exchange_strong(T volatile* obj, T* exp, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        if (std::memcmp(const_cast<T*>(obj), exp, sizeof(T)) == 0) // if (*obj == *exp)
 | 
						|
        {
 | 
						|
            std::memcpy(const_cast<T*>(obj), &desr, sizeof(T)); // *obj = desr;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        std::memcpy(exp, const_cast<T*>(obj), sizeof(T)); // *exp = *obj;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // May spuriously return false (even if *obj == *exp)
 | 
						|
    template <class T>
 | 
						|
    bool __atomic_compare_exchange_weak(T volatile* obj, T* exp, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        if (std::memcmp(const_cast<T*>(obj), exp, sizeof(T)) == 0) // if (*obj == *exp)
 | 
						|
        {
 | 
						|
            std::memcpy(const_cast<T*>(obj), &desr, sizeof(T)); // *obj = desr;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        std::memcpy(exp, const_cast<T*>(obj), sizeof(T)); // *exp = *obj;
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_add(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj += operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_sub(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj -= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_and(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj &= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_or(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj |= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_xor(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj ^= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    void* __atomic_fetch_add(void* volatile* obj, ptrdiff_t operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        void* r = *obj;
 | 
						|
        (char*&)(*obj) += operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    void* __atomic_fetch_sub(void* volatile* obj, ptrdiff_t operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        void* r = *obj;
 | 
						|
        (char*&)(*obj) -= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    void __atomic_thread_fence() {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
    }
 | 
						|
 | 
						|
    void __atomic_signal_fence() {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
Design B: Something in between
 | 
						|
==============================
 | 
						|
This is a variation of design A which puts the burden on the library to arrange
 | 
						|
for the correct manipulation of the run time memory ordering arguments, and only
 | 
						|
calls the compiler for well-defined memory orderings. I think of this design as
 | 
						|
the worst of A and C, instead of the best of A and C. But I offer it as an
 | 
						|
option in the spirit of completeness.
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    bool __atomic_is_lock_free(const type* atomic_obj);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    type __atomic_load_relaxed(const volatile type* atomic_obj);
 | 
						|
    type __atomic_load_consume(const volatile type* atomic_obj);
 | 
						|
    type __atomic_load_acquire(const volatile type* atomic_obj);
 | 
						|
    type __atomic_load_seq_cst(const volatile type* atomic_obj);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    type __atomic_store_relaxed(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_store_release(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_store_seq_cst(volatile type* atomic_obj, type desired);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    type __atomic_exchange_relaxed(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_exchange_consume(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_exchange_acquire(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_exchange_release(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_exchange_acq_rel(volatile type* atomic_obj, type desired);
 | 
						|
    type __atomic_exchange_seq_cst(volatile type* atomic_obj, type desired);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    bool __atomic_compare_exchange_strong_relaxed_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_consume_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_consume_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_acquire_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_acquire_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_acquire_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_release_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_release_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_release_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_acq_rel_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_acq_rel_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_acq_rel_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_seq_cst_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_seq_cst_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_seq_cst_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_strong_seq_cst_seq_cst(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
 | 
						|
    // type must be trivially copyable
 | 
						|
    bool __atomic_compare_exchange_weak_relaxed_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_consume_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_consume_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_acquire_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_acquire_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_acquire_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_release_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_release_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_release_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_acq_rel_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_acq_rel_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_acq_rel_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_seq_cst_relaxed(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_seq_cst_consume(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_seq_cst_acquire(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
    bool __atomic_compare_exchange_weak_seq_cst_seq_cst(volatile type* atomic_obj,
 | 
						|
                                                        type* expected,
 | 
						|
                                                        type desired);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    type __atomic_fetch_add_relaxed(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_add_consume(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_add_acquire(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_add_release(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_add_acq_rel(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_add_seq_cst(volatile type* atomic_obj, type operand);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    type __atomic_fetch_sub_relaxed(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_sub_consume(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_sub_acquire(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_sub_release(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_sub_acq_rel(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_sub_seq_cst(volatile type* atomic_obj, type operand);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    type __atomic_fetch_and_relaxed(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_and_consume(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_and_acquire(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_and_release(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_and_acq_rel(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_and_seq_cst(volatile type* atomic_obj, type operand);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    type __atomic_fetch_or_relaxed(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_or_consume(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_or_acquire(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_or_release(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_or_acq_rel(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_or_seq_cst(volatile type* atomic_obj, type operand);
 | 
						|
 | 
						|
    // type is one of: 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
 | 
						|
    type __atomic_fetch_xor_relaxed(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_xor_consume(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_xor_acquire(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_xor_release(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_xor_acq_rel(volatile type* atomic_obj, type operand);
 | 
						|
    type __atomic_fetch_xor_seq_cst(volatile type* atomic_obj, type operand);
 | 
						|
 | 
						|
    void* __atomic_fetch_add_relaxed(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_add_consume(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_add_acquire(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_add_release(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_add_acq_rel(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_add_seq_cst(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
 | 
						|
    void* __atomic_fetch_sub_relaxed(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_sub_consume(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_sub_acquire(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_sub_release(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_sub_acq_rel(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
    void* __atomic_fetch_sub_seq_cst(void* volatile* atomic_obj, ptrdiff_t operand);
 | 
						|
 | 
						|
    void __atomic_thread_fence_relaxed();
 | 
						|
    void __atomic_thread_fence_consume();
 | 
						|
    void __atomic_thread_fence_acquire();
 | 
						|
    void __atomic_thread_fence_release();
 | 
						|
    void __atomic_thread_fence_acq_rel();
 | 
						|
    void __atomic_thread_fence_seq_cst();
 | 
						|
 | 
						|
    void __atomic_signal_fence_relaxed();
 | 
						|
    void __atomic_signal_fence_consume();
 | 
						|
    void __atomic_signal_fence_acquire();
 | 
						|
    void __atomic_signal_fence_release();
 | 
						|
    void __atomic_signal_fence_acq_rel();
 | 
						|
    void __atomic_signal_fence_seq_cst();
 | 
						|
 | 
						|
Design C: Minimal work for the front end
 | 
						|
========================================
 | 
						|
The ``<atomic>`` header is one of the most closely coupled headers to the compiler.
 | 
						|
Ideally when you invoke any function from ``<atomic>``, it should result in highly
 | 
						|
optimized assembly being inserted directly into your application -- assembly that
 | 
						|
is not otherwise representable by higher level C or C++ expressions. The design of
 | 
						|
the libc++ ``<atomic>`` header started with this goal in mind. A secondary, but
 | 
						|
still very important goal is that the compiler should have to do minimal work to
 | 
						|
facilitate the implementation of ``<atomic>``.  Without this second goal, then
 | 
						|
practically speaking, the libc++ ``<atomic>`` header would be doomed to be a
 | 
						|
barely supported, second class citizen on almost every platform.
 | 
						|
 | 
						|
Goals:
 | 
						|
 | 
						|
- Optimal code generation for atomic operations
 | 
						|
- Minimal effort for the compiler to achieve goal 1 on any given platform
 | 
						|
- Conformance to the C++0X draft standard
 | 
						|
 | 
						|
The purpose of this document is to inform compiler writers what they need to do
 | 
						|
to enable a high performance libc++ ``<atomic>`` with minimal effort.
 | 
						|
 | 
						|
The minimal work that must be done for a conforming ``<atomic>``
 | 
						|
----------------------------------------------------------------
 | 
						|
The only "atomic" operations that must actually be lock free in
 | 
						|
``<atomic>`` are represented by the following compiler intrinsics:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    __atomic_flag__ __atomic_exchange_seq_cst(__atomic_flag__ volatile* obj, __atomic_flag__ desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        __atomic_flag__ result = *obj;
 | 
						|
        *obj = desr;
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
 | 
						|
    void __atomic_store_seq_cst(__atomic_flag__ volatile* obj, __atomic_flag__ desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        *obj = desr;
 | 
						|
    }
 | 
						|
 | 
						|
Where:
 | 
						|
 | 
						|
- If ``__has_feature(__atomic_flag)`` evaluates to 1 in the preprocessor then
 | 
						|
  the compiler must define ``__atomic_flag__`` (e.g. as a typedef to ``int``).
 | 
						|
- If ``__has_feature(__atomic_flag)`` evaluates to 0 in the preprocessor then
 | 
						|
  the library defines ``__atomic_flag__`` as a typedef to ``bool``.
 | 
						|
- To communicate that the above intrinsics are available, the compiler must
 | 
						|
  arrange for ``__has_feature`` to return 1 when fed the intrinsic name
 | 
						|
  appended with an '_' and the mangled type name of ``__atomic_flag__``.
 | 
						|
 | 
						|
For example if ``__atomic_flag__`` is ``unsigned int``:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    // __has_feature(__atomic_flag) == 1
 | 
						|
    // __has_feature(__atomic_exchange_seq_cst_j) == 1
 | 
						|
    // __has_feature(__atomic_store_seq_cst_j) == 1
 | 
						|
 | 
						|
    typedef unsigned int __atomic_flag__;
 | 
						|
 | 
						|
    unsigned int __atomic_exchange_seq_cst(unsigned int volatile*, unsigned int) {
 | 
						|
        // ...
 | 
						|
    }
 | 
						|
 | 
						|
    void __atomic_store_seq_cst(unsigned int volatile*, unsigned int) {
 | 
						|
        // ...
 | 
						|
    }
 | 
						|
 | 
						|
That's it! Compiler writers do the above and you've got a fully conforming
 | 
						|
(though sub-par performance) ``<atomic>`` header!
 | 
						|
 | 
						|
 | 
						|
Recommended work for a higher performance ``<atomic>``
 | 
						|
------------------------------------------------------
 | 
						|
It would be good if the above intrinsics worked with all integral types plus
 | 
						|
``void*``. Because this may not be possible to do in a lock-free manner for
 | 
						|
all integral types on all platforms, a compiler must communicate each type that
 | 
						|
an intrinsic works with. For example, if ``__atomic_exchange_seq_cst`` works
 | 
						|
for all types except for ``long long`` and ``unsigned long long`` then:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_b) == 1  // bool
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_c) == 1  // char
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_a) == 1  // signed char
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_h) == 1  // unsigned char
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_Ds) == 1 // char16_t
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_Di) == 1 // char32_t
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_w) == 1  // wchar_t
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_s) == 1  // short
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_t) == 1  // unsigned short
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_i) == 1  // int
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_j) == 1  // unsigned int
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_l) == 1  // long
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_m) == 1  // unsigned long
 | 
						|
    __has_feature(__atomic_exchange_seq_cst_Pv) == 1 // void*
 | 
						|
 | 
						|
Note that only the ``__has_feature`` flag is decorated with the argument
 | 
						|
type. The name of the compiler intrinsic is not decorated, but instead works
 | 
						|
like a C++ overloaded function.
 | 
						|
 | 
						|
Additionally, there are other intrinsics besides ``__atomic_exchange_seq_cst``
 | 
						|
and ``__atomic_store_seq_cst``. They are optional. But if the compiler can
 | 
						|
generate faster code than provided by the library, then clients will benefit
 | 
						|
from the compiler writer's expertise and knowledge of the targeted platform.
 | 
						|
 | 
						|
Below is the complete list of *sequentially consistent* intrinsics, and
 | 
						|
their library implementations. Template syntax is used to indicate the desired
 | 
						|
overloading for integral and ``void*`` types. The template does not represent a
 | 
						|
requirement that the intrinsic operate on **any** type!
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    // T is one of:
 | 
						|
    // bool, 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, void*
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_load_seq_cst(T const volatile* obj) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        return *obj;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    void __atomic_store_seq_cst(T volatile* obj, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        *obj = desr;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_exchange_seq_cst(T volatile* obj, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj = desr;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    bool __atomic_compare_exchange_strong_seq_cst_seq_cst(T volatile* obj, T* exp, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        if (std::memcmp(const_cast<T*>(obj), exp, sizeof(T)) == 0) {
 | 
						|
            std::memcpy(const_cast<T*>(obj), &desr, sizeof(T));
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        std::memcpy(exp, const_cast<T*>(obj), sizeof(T));
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    bool __atomic_compare_exchange_weak_seq_cst_seq_cst(T volatile* obj, T* exp, T desr) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        if (std::memcmp(const_cast<T*>(obj), exp, sizeof(T)) == 0)
 | 
						|
        {
 | 
						|
            std::memcpy(const_cast<T*>(obj), &desr, sizeof(T));
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        std::memcpy(exp, const_cast<T*>(obj), sizeof(T));
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // T is one of:
 | 
						|
    // 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
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_add_seq_cst(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj += operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_sub_seq_cst(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj -= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_and_seq_cst(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj &= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_or_seq_cst(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj |= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    template <class T>
 | 
						|
    T __atomic_fetch_xor_seq_cst(T volatile* obj, T operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        T r = *obj;
 | 
						|
        *obj ^= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    void* __atomic_fetch_add_seq_cst(void* volatile* obj, ptrdiff_t operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        void* r = *obj;
 | 
						|
        (char*&)(*obj) += operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    void* __atomic_fetch_sub_seq_cst(void* volatile* obj, ptrdiff_t operand) {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
        void* r = *obj;
 | 
						|
        (char*&)(*obj) -= operand;
 | 
						|
        return r;
 | 
						|
    }
 | 
						|
 | 
						|
    void __atomic_thread_fence_seq_cst() {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
    }
 | 
						|
 | 
						|
    void __atomic_signal_fence_seq_cst() {
 | 
						|
        unique_lock<mutex> _(some_mutex);
 | 
						|
    }
 | 
						|
 | 
						|
One should consult the (currently draft) `C++ Standard <https://wg21.link/n3126>`_
 | 
						|
for the details of the definitions for these operations. For example,
 | 
						|
``__atomic_compare_exchange_weak_seq_cst_seq_cst`` is allowed to fail
 | 
						|
spuriously while ``__atomic_compare_exchange_strong_seq_cst_seq_cst`` is not.
 | 
						|
 | 
						|
If on your platform the lock-free definition of ``__atomic_compare_exchange_weak_seq_cst_seq_cst``
 | 
						|
would be the same as ``__atomic_compare_exchange_strong_seq_cst_seq_cst``, you may omit the
 | 
						|
``__atomic_compare_exchange_weak_seq_cst_seq_cst`` intrinsic without a performance cost. The
 | 
						|
library will prefer your implementation of ``__atomic_compare_exchange_strong_seq_cst_seq_cst``
 | 
						|
over its own definition for implementing ``__atomic_compare_exchange_weak_seq_cst_seq_cst``.
 | 
						|
That is, the library will arrange for ``__atomic_compare_exchange_weak_seq_cst_seq_cst`` to call
 | 
						|
``__atomic_compare_exchange_strong_seq_cst_seq_cst`` if you supply an intrinsic for the strong
 | 
						|
version but not the weak.
 | 
						|
 | 
						|
Taking advantage of weaker memory synchronization
 | 
						|
-------------------------------------------------
 | 
						|
So far, all of the intrinsics presented require a **sequentially consistent** memory ordering.
 | 
						|
That is, no loads or stores can move across the operation (just as if the library had locked
 | 
						|
that internal mutex). But ``<atomic>`` supports weaker memory ordering operations. In all,
 | 
						|
there are six memory orderings (listed here from strongest to weakest):
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    memory_order_seq_cst
 | 
						|
    memory_order_acq_rel
 | 
						|
    memory_order_release
 | 
						|
    memory_order_acquire
 | 
						|
    memory_order_consume
 | 
						|
    memory_order_relaxed
 | 
						|
 | 
						|
(See the `C++ Standard <https://wg21.link/n3126>`_ for the detailed definitions of each of these orderings).
 | 
						|
 | 
						|
On some platforms, the compiler vendor can offer some or even all of the above
 | 
						|
intrinsics at one or more weaker levels of memory synchronization. This might
 | 
						|
lead for example to not issuing an ``mfence`` instruction on the x86.
 | 
						|
 | 
						|
If the compiler does not offer any given operation, at any given memory ordering
 | 
						|
level, the library will automatically attempt to call the next highest memory
 | 
						|
ordering operation. This continues up to ``seq_cst``, and if that doesn't
 | 
						|
exist, then the library takes over and does the job with a ``mutex``. This
 | 
						|
is a compile-time search and selection operation. At run time, the application
 | 
						|
will only see the few inlined assembly instructions for the selected intrinsic.
 | 
						|
 | 
						|
Each intrinsic is appended with the 7-letter name of the memory ordering it
 | 
						|
addresses. For example a ``load`` with ``relaxed`` ordering is defined by:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    T __atomic_load_relaxed(const volatile T* obj);
 | 
						|
 | 
						|
And announced with:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    __has_feature(__atomic_load_relaxed_b) == 1  // bool
 | 
						|
    __has_feature(__atomic_load_relaxed_c) == 1  // char
 | 
						|
    __has_feature(__atomic_load_relaxed_a) == 1  // signed char
 | 
						|
    ...
 | 
						|
 | 
						|
The ``__atomic_compare_exchange_strong(weak)`` intrinsics are parameterized
 | 
						|
on two memory orderings. The first ordering applies when the operation returns
 | 
						|
``true`` and the second ordering applies when the operation returns ``false``.
 | 
						|
 | 
						|
Not every memory ordering is appropriate for every operation. ``exchange``
 | 
						|
and the ``fetch_XXX`` operations support all 6. But ``load`` only supports
 | 
						|
``relaxed``, ``consume``, ``acquire`` and ``seq_cst``. ``store`` only supports
 | 
						|
``relaxed``, ``release``, and ``seq_cst``. The ``compare_exchange`` operations
 | 
						|
support the following 16 combinations out of the possible 36:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    relaxed_relaxed
 | 
						|
    consume_relaxed
 | 
						|
    consume_consume
 | 
						|
    acquire_relaxed
 | 
						|
    acquire_consume
 | 
						|
    acquire_acquire
 | 
						|
    release_relaxed
 | 
						|
    release_consume
 | 
						|
    release_acquire
 | 
						|
    acq_rel_relaxed
 | 
						|
    acq_rel_consume
 | 
						|
    acq_rel_acquire
 | 
						|
    seq_cst_relaxed
 | 
						|
    seq_cst_consume
 | 
						|
    seq_cst_acquire
 | 
						|
    seq_cst_seq_cst
 | 
						|
 | 
						|
Again, the compiler supplies intrinsics only for the strongest orderings where
 | 
						|
it can make a difference. The library takes care of calling the weakest
 | 
						|
supplied intrinsic that is as strong or stronger than the customer asked for.
 | 
						|
 | 
						|
Note about ABI
 | 
						|
==============
 | 
						|
With any design, the (back end) compiler writer should note that the decision to
 | 
						|
implement lock-free operations on any given type (or not) is an ABI-binding decision.
 | 
						|
One can not change from treating a type as not lock free, to lock free (or vice-versa)
 | 
						|
without breaking your ABI.
 | 
						|
 | 
						|
For example:
 | 
						|
 | 
						|
**TU1.cpp**:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    extern atomic<long long> A;
 | 
						|
    int foo() { return A.compare_exchange_strong(w, x); }
 | 
						|
 | 
						|
 | 
						|
**TU2.cpp**:
 | 
						|
 | 
						|
.. code-block:: cpp
 | 
						|
 | 
						|
    extern atomic<long long> A;
 | 
						|
    void bar() { return A.compare_exchange_strong(y, z); }
 | 
						|
 | 
						|
If only **one** of these calls to ``compare_exchange_strong`` is implemented with
 | 
						|
mutex-locked code, then that mutex-locked code will not be executed mutually
 | 
						|
exclusively of the one implemented in a lock-free manner.
 |