| // Copyright Microsoft and CHERIoT Contributors. | |
| // SPDX-License-Identifier: MIT | |
| #pragma once | |
| #include <concepts> | |
| #include <futex.h> | |
| #include <type_traits> | |
| __clang_ignored_warning_push("-Watomic-alignment") namespace std | |
| { | |
| enum class memory_order : int | |
| { | |
| relaxed = __ATOMIC_RELAXED, | |
| consume = __ATOMIC_CONSUME, | |
| acquire = __ATOMIC_ACQUIRE, | |
| release = __ATOMIC_RELEASE, | |
| acq_rel = __ATOMIC_ACQ_REL, | |
| seq_cst = __ATOMIC_SEQ_CST, | |
| }; | |
| inline constexpr memory_order memory_order_relaxed = memory_order::relaxed; | |
| inline constexpr memory_order memory_order_consume = memory_order::consume; | |
| inline constexpr memory_order memory_order_acquire = memory_order::acquire; | |
| inline constexpr memory_order memory_order_release = memory_order::release; | |
| inline constexpr memory_order memory_order_acq_rel = memory_order::acq_rel; | |
| inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cst; | |
| namespace detail | |
| { | |
| /** | |
| * Version of atomic<T> for primitive types. This calls the atomics | |
| * support library for everything on platforms with no A extension and | |
| * will use atomic instructions on ones that do. | |
| * | |
| * This differs from std::atomic in two intentional ways: | |
| * | |
| * - The `wait` and `notify_*` methods are defined only on 4-byte | |
| * values. If there is a requirement for anything else, we should | |
| * extend the futex APIs in the scheduler to deal with other types. | |
| * - The `wait` call has a non-standard extension that handles takes a | |
| * CHERIoT timeout parameter. | |
| * | |
| * Any other divergence is a bug. | |
| * | |
| * This is a base class that is extended for arithmetic and pointer | |
| * types. | |
| */ | |
| template<typename T> | |
| class primitive_atomic | |
| { | |
| /** | |
| * SFINAE helper to give us the underlying type of enums and the | |
| * raw type of everything else. | |
| */ | |
| template<typename U, bool = std::is_enum_v<U>> | |
| struct underlying_type | |
| { | |
| using type = U; | |
| }; | |
| template<typename U> | |
| struct underlying_type<U, true> : ::std::underlying_type<U> | |
| { | |
| }; | |
| protected: | |
| typename underlying_type<T>::type value; | |
| static_assert(std::is_arithmetic_v<T> || std::is_enum_v<T> || | |
| std::is_pointer_v<T> || std::is_null_pointer_v<T>, | |
| "Invalid type for primitive atomic"); | |
| __always_inline auto *pointer_for_intrinsics(T *pointer) | |
| { | |
| if constexpr (std::is_enum_v<T>) | |
| { | |
| return static_cast<std::underlying_type_t<T> *>(pointer); | |
| } | |
| else | |
| { | |
| return pointer; | |
| } | |
| } | |
| static decltype(value) *as_underlying(T *v) | |
| { | |
| return reinterpret_cast<decltype(value) *>(v); | |
| } | |
| static decltype(value) as_underlying(T v) | |
| { | |
| return static_cast<decltype(value)>(v); | |
| } | |
| public: | |
| using value_type = T; | |
| static constexpr bool is_always_lock_free = true; | |
| __always_inline bool is_lock_free() const noexcept | |
| { | |
| return true; | |
| } | |
| __always_inline bool is_lock_free() const volatile noexcept | |
| { | |
| return true; | |
| } | |
| constexpr primitive_atomic() noexcept = default; | |
| __always_inline constexpr primitive_atomic(T desired) noexcept | |
| { | |
| value = desired; | |
| } | |
| primitive_atomic(const primitive_atomic &) = delete; | |
| __always_inline T operator=(T desired) noexcept | |
| { | |
| store(desired); | |
| return desired; | |
| } | |
| __always_inline T operator=(T desired) volatile noexcept | |
| { | |
| return *const_cast<primitive_atomic<T> *>(this) = desired; | |
| } | |
| primitive_atomic &operator=(const primitive_atomic &) = delete; | |
| primitive_atomic & | |
| operator=(const primitive_atomic &) volatile = delete; | |
| __always_inline void | |
| store(T desired, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| __atomic_store_n(&value, as_underlying(desired), int(order)); | |
| } | |
| __always_inline void | |
| store(T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<primitive_atomic<T> *>(this)->store(desired, order); | |
| } | |
| __always_inline T | |
| load(memory_order order = memory_order_seq_cst) const noexcept | |
| { | |
| return __atomic_load_n(&value, int(order)); | |
| } | |
| __always_inline T load( | |
| memory_order order = memory_order_seq_cst) const volatile noexcept | |
| { | |
| return const_cast<primitive_atomic<T> *>(this)->load(order); | |
| } | |
| __always_inline operator T() const noexcept | |
| { | |
| return load(); | |
| } | |
| __always_inline operator T() const volatile noexcept | |
| { | |
| return load(); | |
| } | |
| __always_inline T | |
| exchange(T desired, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return T(__atomic_exchange_n( | |
| &value, as_underlying(desired), int(order))); | |
| } | |
| __always_inline T exchange( | |
| T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| return const_cast<primitive_atomic<T> *>(this)->exchange( | |
| desired, order); | |
| } | |
| __always_inline bool | |
| compare_exchange_weak(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) noexcept | |
| { | |
| __atomic_compare_exchange_n(&value, | |
| as_underlying(&expected), | |
| as_underlying(desired), | |
| true, | |
| int(success), | |
| int(failure)); | |
| } | |
| __always_inline bool | |
| compare_exchange_weak(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) volatile noexcept | |
| { | |
| return const_cast<primitive_atomic<T> *>(this) | |
| ->compare_exchange_weak(expected, desired, success, failure); | |
| } | |
| __always_inline bool compare_exchange_weak( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return compare_exchange_weak(expected, desired, order, order); | |
| } | |
| __always_inline bool compare_exchange_weak( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| return const_cast<primitive_atomic<T> *>(this) | |
| ->compare_exchange_weak(expected, desired, order); | |
| } | |
| __always_inline bool | |
| compare_exchange_strong(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) noexcept | |
| { | |
| return __atomic_compare_exchange_n(&value, | |
| as_underlying(&expected), | |
| as_underlying(desired), | |
| false, | |
| int(success), | |
| int(failure)); | |
| } | |
| __always_inline bool | |
| compare_exchange_strong(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) volatile noexcept | |
| { | |
| return const_cast<primitive_atomic<T> *>(this) | |
| ->compare_exchange_strong( | |
| expected, desired, success, failure); | |
| } | |
| __always_inline bool compare_exchange_strong( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return compare_exchange_strong(expected, desired, order, order); | |
| } | |
| __always_inline bool compare_exchange_strong( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| return const_cast<primitive_atomic<T> *>(this) | |
| ->compare_exchange_strong(expected, desired, order); | |
| } | |
| __always_inline void | |
| wait(T old, | |
| memory_order order = memory_order::seq_cst) const noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| futex_wait(reinterpret_cast<const uint32_t *>(&value), | |
| reinterpret_cast<uint32_t>(as_underlying(old))); | |
| } | |
| __always_inline int | |
| wait(Timeout *timeout, | |
| T old, | |
| memory_order order = memory_order::seq_cst, | |
| FutexWaitFlags flags = FutexNone) const noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| return futex_timed_wait( | |
| timeout, | |
| reinterpret_cast<const uint32_t *>(&value), | |
| static_cast<uint32_t>(as_underlying(old)), | |
| flags); | |
| } | |
| __always_inline int | |
| wait(Timeout *timeout, T old, FutexWaitFlags flags) const noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| return wait(timeout, old, memory_order::seq_cst, flags); | |
| } | |
| __always_inline void | |
| wait(T old, memory_order order = memory_order::seq_cst) const | |
| volatile noexcept requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| const_cast<primitive_atomic<T> *>(this)->wait(old, order); | |
| } | |
| __always_inline void notify_one() noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| futex_wake(reinterpret_cast<uint32_t *>(&value), 1); | |
| } | |
| __always_inline void notify_one() volatile noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| const_cast<primitive_atomic<T> *>(this)->notify_one(); | |
| } | |
| __always_inline void notify_all() noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| futex_wake(reinterpret_cast<uint32_t *>(&value), | |
| std::numeric_limits<uint32_t>::max()); | |
| } | |
| __always_inline void notify_all() volatile noexcept | |
| requires(sizeof(T) == sizeof(uint32_t)) | |
| { | |
| const_cast<primitive_atomic<T> *>(this)->notify_all(); | |
| } | |
| }; | |
| /** | |
| * Version of atomic for arithmetic types. This adds the arithmetic | |
| * methods. | |
| */ | |
| template<typename T> | |
| class arithmetic_atomic : public primitive_atomic<T> | |
| { | |
| public: | |
| using primitive_atomic<T>::primitive_atomic; | |
| using primitive_atomic<T>::operator=; | |
| using difference_type = typename primitive_atomic<T>::value_type; | |
| __always_inline T | |
| fetch_add(T arg, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_add(&this->value, arg, int(order)); | |
| } | |
| __always_inline T fetch_add( | |
| T arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<arithmetic_atomic<T> *>(this)->fetch_add(arg, order); | |
| } | |
| __always_inline T | |
| fetch_sub(T arg, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_sub(&this->value, arg, int(order)); | |
| } | |
| __always_inline T fetch_sub( | |
| T arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<arithmetic_atomic<T> *>(this)->fetch_sub(arg, order); | |
| } | |
| __always_inline T | |
| fetch_and(T arg, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_and(&this->value, arg, int(order)); | |
| } | |
| __always_inline T fetch_and( | |
| T arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<arithmetic_atomic<T> *>(this)->fetch_and(arg, order); | |
| } | |
| __always_inline T | |
| fetch_or(T arg, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_or(&this->value, arg, int(order)); | |
| } | |
| __always_inline T fetch_or( | |
| T arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<arithmetic_atomic<T> *>(this)->fetch_or(arg, order); | |
| } | |
| __always_inline T | |
| fetch_xor(T arg, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_xor(&this->value, arg, int(order)); | |
| } | |
| __always_inline T fetch_xor( | |
| T arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<arithmetic_atomic<T> *>(this)->fetch_xor(arg, order); | |
| } | |
| __always_inline T operator++() noexcept | |
| { | |
| return fetch_add(1) + 1; | |
| } | |
| __always_inline T operator++() volatile noexcept | |
| { | |
| return fetch_add(1) + 1; | |
| } | |
| __always_inline T operator++(int) noexcept | |
| { | |
| return fetch_add(1); | |
| } | |
| __always_inline T operator++(int) volatile noexcept | |
| { | |
| return fetch_add(1); | |
| } | |
| __always_inline T operator--() noexcept | |
| { | |
| return fetch_sub(1) - 1; | |
| } | |
| __always_inline T operator--() volatile noexcept | |
| { | |
| return fetch_sub(1) - 1; | |
| } | |
| __always_inline T operator--(int) noexcept | |
| { | |
| return fetch_sub(1); | |
| } | |
| __always_inline T operator--(int) volatile noexcept | |
| { | |
| return fetch_sub(1); | |
| } | |
| __always_inline T operator+=(T arg) noexcept | |
| { | |
| return fetch_add(arg) + arg; | |
| } | |
| __always_inline T operator+=(T arg) volatile noexcept | |
| { | |
| return fetch_add(arg) + arg; | |
| } | |
| __always_inline T operator-=(T arg) noexcept | |
| { | |
| return fetch_sub(arg) - arg; | |
| } | |
| __always_inline T operator-=(T arg) volatile noexcept | |
| { | |
| return fetch_sub(arg) - arg; | |
| } | |
| __always_inline T operator&=(T arg) noexcept | |
| { | |
| return fetch_and(arg) & arg; | |
| } | |
| __always_inline T operator&=(T arg) volatile noexcept | |
| { | |
| return fetch_and(arg) & arg; | |
| } | |
| __always_inline T operator|=(T arg) noexcept | |
| { | |
| return fetch_or(arg) | arg; | |
| } | |
| __always_inline T operator|=(T arg) volatile noexcept | |
| { | |
| return fetch_or(arg) | arg; | |
| } | |
| __always_inline T operator^=(T arg) noexcept | |
| { | |
| return fetch_xor(arg) ^ arg; | |
| } | |
| __always_inline T operator^=(T arg) volatile noexcept | |
| { | |
| return fetch_xor(arg) ^ arg; | |
| } | |
| }; | |
| /** | |
| * Version of atomic for pointer types. This adds pointer arithmetic | |
| * methods. | |
| */ | |
| template<typename T> | |
| class pointer_atomic : primitive_atomic<T> | |
| { | |
| public: | |
| using primitive_atomic<T>::primitive_atomic; | |
| using difference_type = std::ptrdiff_t; | |
| T *fetch_add(std::ptrdiff_t arg, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_add( | |
| &this->value, arg * sizeof(T), int(order)); | |
| } | |
| T *fetch_add( | |
| std::ptrdiff_t arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<pointer_atomic<T> *>(this)->fetch_add(arg, | |
| int(order)); | |
| } | |
| T *fetch_sub(std::ptrdiff_t arg, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return __atomic_fetch_sub( | |
| &this->value, arg * sizeof(T), int(order)); | |
| } | |
| T *fetch_sub( | |
| std::ptrdiff_t arg, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<pointer_atomic<T> *>(this)->fetch_sub(arg, | |
| int(order)); | |
| } | |
| __always_inline T *operator++() noexcept | |
| { | |
| return fetch_add(1) + 1; | |
| } | |
| __always_inline T *operator++() volatile noexcept | |
| { | |
| return fetch_add(1) + 1; | |
| } | |
| __always_inline T *operator++(int) noexcept | |
| { | |
| return fetch_add(1); | |
| } | |
| __always_inline T *operator++(int) volatile noexcept | |
| { | |
| return fetch_add(1); | |
| } | |
| __always_inline T *operator--() noexcept | |
| { | |
| return fetch_sub(1) - 1; | |
| } | |
| __always_inline T *operator--() volatile noexcept | |
| { | |
| return fetch_sub(1) - 1; | |
| } | |
| __always_inline T *operator--(int) noexcept | |
| { | |
| return fetch_sub(1); | |
| } | |
| __always_inline T *operator--(int) volatile noexcept | |
| { | |
| return fetch_sub(1); | |
| } | |
| __always_inline T *operator+=(std::ptrdiff_t arg) noexcept | |
| { | |
| return fetch_add(arg) + arg; | |
| } | |
| __always_inline T *operator+=(std::ptrdiff_t arg) volatile noexcept | |
| { | |
| return fetch_add(arg) + arg; | |
| } | |
| __always_inline T *operator-=(std::ptrdiff_t arg) noexcept | |
| { | |
| return fetch_sub(arg) - arg; | |
| } | |
| __always_inline T *operator-=(std::ptrdiff_t arg) volatile noexcept | |
| { | |
| return fetch_sub(arg) - arg; | |
| } | |
| }; | |
| /** | |
| * Simple flag lock. This uses `primitive_atomic` to build a trivial | |
| * lock. | |
| */ | |
| class flag_lock | |
| { | |
| /** | |
| * Possible states of the flag lock. | |
| */ | |
| enum class LockState : uint32_t | |
| { | |
| /// Lock is not held. | |
| Unlocked, | |
| /// Lock is held, no waiters. | |
| Locked, | |
| /// Lock is held and one or more waiters exist. | |
| LockedWithWaiters | |
| }; | |
| /// The lock state. | |
| primitive_atomic<LockState> lockWord; | |
| public: | |
| /** | |
| * Acquire the lock. Blocks indefinitely. | |
| */ | |
| __noinline void lock() | |
| { | |
| LockState old = LockState::Unlocked; | |
| while (true) | |
| { | |
| switch (old) | |
| { | |
| // If the lock is not held, try to acquire it and return | |
| // if we can. | |
| case LockState::Unlocked: | |
| if (lockWord.compare_exchange_strong( | |
| old, LockState::Locked)) | |
| { | |
| return; | |
| } | |
| break; | |
| // If the lock is held, mark it as having waiters | |
| // and then wait. | |
| case LockState::Locked: | |
| lockWord.exchange(LockState::LockedWithWaiters); | |
| [[fallthrough]]; | |
| // If the lock is blocked with waiters, sleep | |
| case LockState::LockedWithWaiters: | |
| lockWord.wait(LockState::LockedWithWaiters); | |
| } | |
| } | |
| } | |
| /** | |
| * Release the lock, waking any waiters if there are any. | |
| */ | |
| __noinline void unlock() | |
| { | |
| auto old = lockWord.exchange(LockState::Unlocked); | |
| if (old == LockState::LockedWithWaiters) | |
| { | |
| lockWord.notify_all(); | |
| } | |
| } | |
| }; | |
| /** | |
| * Fallback `atomic` implementation that uses a lock to protect the | |
| * value. | |
| */ | |
| template<typename T> | |
| class locked_atomic | |
| { | |
| private: | |
| /// The atomic value. | |
| T value; | |
| /// Lock protecting this object, at the end so that it can go into | |
| /// padding. | |
| mutable flag_lock lock; | |
| struct guard | |
| { | |
| flag_lock &lock; | |
| __always_inline ~guard() | |
| { | |
| lock.unlock(); | |
| } | |
| }; | |
| __always_inline guard acquire_lock() | |
| { | |
| lock.lock(); | |
| return {lock}; | |
| } | |
| public: | |
| using value_type = T; | |
| static constexpr bool is_always_lock_free = false; | |
| __always_inline bool is_lock_free() const noexcept | |
| { | |
| return false; | |
| } | |
| __always_inline bool is_lock_free() const volatile noexcept | |
| { | |
| return false; | |
| } | |
| constexpr locked_atomic() noexcept = default; | |
| __always_inline constexpr locked_atomic(T desired) noexcept | |
| { | |
| value = desired; | |
| } | |
| locked_atomic(const locked_atomic &) = delete; | |
| __always_inline T operator=(T desired) noexcept | |
| { | |
| auto g = acquire_lock(); | |
| value = desired; | |
| return desired; | |
| } | |
| __always_inline T operator=(T desired) volatile noexcept | |
| { | |
| return *const_cast<locked_atomic<T> *>(this) = desired; | |
| } | |
| locked_atomic &operator=(const locked_atomic &) = delete; | |
| locked_atomic &operator=(const locked_atomic &) volatile = delete; | |
| __always_inline void | |
| store(T desired, memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| auto g = acquire_lock(); | |
| value = desired; | |
| } | |
| __always_inline void | |
| store(T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| const_cast<locked_atomic<T> *>(this)->store(desired, order); | |
| } | |
| __always_inline T | |
| load(memory_order order = memory_order_seq_cst) const noexcept | |
| { | |
| auto g = acquire_lock(); | |
| return value; | |
| } | |
| __always_inline T load( | |
| memory_order order = memory_order_seq_cst) const volatile noexcept | |
| { | |
| return const_cast<locked_atomic<T> *>(this)->load(order); | |
| } | |
| __always_inline operator T() const noexcept | |
| { | |
| return load(); | |
| } | |
| __always_inline operator T() const volatile noexcept | |
| { | |
| return load(); | |
| } | |
| __always_inline T | |
| exchange(T desired, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| auto g = acquire_lock(); | |
| T tmp = value; | |
| value = desired; | |
| return tmp; | |
| } | |
| __always_inline T exchange( | |
| T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| return const_cast<locked_atomic<T> *>(this)->exchange(desired, | |
| order); | |
| } | |
| __always_inline bool | |
| compare_exchange_weak(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) noexcept | |
| { | |
| auto g = acquire_lock(); | |
| if (value == expected) | |
| { | |
| desired = value; | |
| } | |
| expected = value; | |
| return true; | |
| } | |
| __always_inline bool | |
| compare_exchange_weak(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) volatile noexcept | |
| { | |
| return const_cast<locked_atomic<T> *>(this) | |
| ->compare_exchange_weak(expected, desired, success, failure); | |
| } | |
| __always_inline bool compare_exchange_weak( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return compare_exchange_weak(expected, desired, order, order); | |
| } | |
| __always_inline bool compare_exchange_weak( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| return const_cast<locked_atomic<T> *>(this) | |
| ->compare_exchange_weak(expected, desired, order); | |
| } | |
| __always_inline bool | |
| compare_exchange_strong(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) noexcept | |
| { | |
| return compare_exchange_weak( | |
| expected, desired, success, failure); | |
| } | |
| __always_inline bool | |
| compare_exchange_strong(T &expected, | |
| T desired, | |
| memory_order success, | |
| memory_order failure) volatile noexcept | |
| { | |
| return const_cast<locked_atomic<T> *>(this) | |
| ->compare_exchange_strong( | |
| expected, desired, success, failure); | |
| } | |
| __always_inline bool compare_exchange_strong( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) noexcept | |
| { | |
| return compare_exchange_strong(expected, desired, order, order); | |
| } | |
| __always_inline bool compare_exchange_strong( | |
| T &expected, | |
| T desired, | |
| memory_order order = memory_order_seq_cst) volatile noexcept | |
| { | |
| return const_cast<locked_atomic<T> *>(this) | |
| ->compare_exchange_strong(expected, desired, order); | |
| } | |
| }; | |
| template<typename T> | |
| using atomic = std::conditional_t< | |
| std::is_pointer_v<T>, | |
| detail::pointer_atomic<T>, | |
| std::conditional_t<std::is_arithmetic_v<T>, | |
| detail::arithmetic_atomic<T>, | |
| std::conditional_t<std::is_enum_v<T>, | |
| detail::primitive_atomic<T>, | |
| detail::locked_atomic<T>>>>; | |
| }; // namespace detail | |
| /** | |
| * Select the correct `atomic` implementation based on the type. | |
| */ | |
| template<typename T> | |
| class atomic : public detail::atomic<T> | |
| { | |
| public: | |
| using detail::atomic<T>::atomic; | |
| using detail::atomic<T>::operator=; | |
| }; | |
| template<class T> | |
| void atomic_store(std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired) noexcept | |
| { | |
| return obj->store(desired); | |
| } | |
| template<class T> | |
| void atomic_store(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired) noexcept | |
| { | |
| return obj->store(desired); | |
| } | |
| template<class T> | |
| void atomic_store_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->store(desired, order); | |
| } | |
| template<class T> | |
| void atomic_store_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->store(desired, order); | |
| } | |
| template<class T> | |
| T atomic_load(const std::atomic<T> *obj) noexcept | |
| { | |
| return obj->load(); | |
| } | |
| template<class T> | |
| T atomic_load(const volatile std::atomic<T> *obj) noexcept | |
| { | |
| return obj->load(); | |
| } | |
| template<class T> | |
| T atomic_load_explicit(const std::atomic<T> *obj, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->load(order); | |
| } | |
| template<class T> | |
| T atomic_load_explicit(const volatile std::atomic<T> *obj, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->load(order); | |
| } | |
| template<class T> | |
| T atomic_exchange(std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired) noexcept | |
| { | |
| return obj->exchange(desired); | |
| } | |
| template<class T> | |
| T atomic_exchange(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired) noexcept | |
| { | |
| return obj->exchange(desired); | |
| } | |
| template<class T> | |
| T atomic_exchange_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->exchange(desired, order); | |
| } | |
| template<class T> | |
| T atomic_exchange_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::value_type desired, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->exchange(desired, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_add(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_add(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_add(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_add(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_add_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_add(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_add_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_add(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_sub(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_sub(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_sub(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_sub(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_sub_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_sub(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_sub_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_sub(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_and(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_and(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_and(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_and(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_and_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_and(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_and_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_and(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_or(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_or(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_or(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_or(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_or_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_or(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_or_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_or(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_xor(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_xor(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_xor(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg) noexcept | |
| { | |
| return obj->fetch_xor(arg); | |
| } | |
| template<class T> | |
| T atomic_fetch_xor_explicit(std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_xor(arg, order); | |
| } | |
| template<class T> | |
| T atomic_fetch_xor_explicit(volatile std::atomic<T> * obj, | |
| typename std::atomic<T>::difference_type arg, | |
| std::memory_order order) noexcept | |
| { | |
| return obj->fetch_xor(arg, order); | |
| } | |
| template<class T> | |
| void atomic_wait(const std::atomic<T> *object, | |
| typename std::atomic<T>::value_type old) | |
| { | |
| object->wait(old); | |
| } | |
| template<class T> | |
| void atomic_wait(const volatile std::atomic<T> *object, | |
| typename std::atomic<T>::value_type old) | |
| { | |
| object->wait(old); | |
| } | |
| template<class T> | |
| void atomic_wait_explicit(const std::atomic<T> *object, | |
| typename std::atomic<T>::value_type old, | |
| std::memory_order order) | |
| { | |
| object->wait(old, order); | |
| } | |
| template<class T> | |
| void atomic_wait_explicit(const volatile std::atomic<T> *object, | |
| typename std::atomic<T>::value_type old, | |
| std::memory_order order) | |
| { | |
| object->wait(old, order); | |
| } | |
| template<class T> | |
| void atomic_notify_one(std::atomic<T> * object) | |
| { | |
| object->notify_one(); | |
| } | |
| template<class T> | |
| void atomic_notify_one(volatile std::atomic<T> * object) | |
| { | |
| object->notify_one(); | |
| } | |
| template<class T> | |
| void atomic_notify_all(std::atomic<T> * object) | |
| { | |
| object->notify_all(); | |
| } | |
| template<class T> | |
| void atomic_notify_all(volatile std::atomic<T> * object) | |
| { | |
| object->notify_all(); | |
| } | |
| } // namespace std | |
| __clang_ignored_warning_pop() |