blob: 412f09e96ec76d67bed159c7e17b51db0ae742bd [file] [log] [blame]
// 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()