312 lines
8.8 KiB
C++
312 lines
8.8 KiB
C++
/***
|
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: auAtomic.hpp
|
|
Date: 2022-2-1
|
|
Author: Reece
|
|
***/
|
|
#pragma once
|
|
|
|
template <class T>
|
|
struct AuAtomicUtils
|
|
{
|
|
/**
|
|
* @brief Generic bitwise (1 << offset)
|
|
* @return original value
|
|
* @warning T is bound by platform and compiler constraints
|
|
*/
|
|
static T Set(T *in, AuUInt8 offset);
|
|
|
|
/**
|
|
* @brief Adds addend to in
|
|
* @return updated value
|
|
* @warning T is bound by platform and compiler constraints
|
|
*/
|
|
static T Add(T *in, T addend);
|
|
|
|
/**
|
|
* @brief Subtracts the minuend from in
|
|
* @return updated value
|
|
* @warning T is bound by platform and compiler constraints
|
|
*/
|
|
static T Sub(T *in, T minuend);
|
|
|
|
/**
|
|
* @brief Generic compare exchange
|
|
* @param replace replacement value for in if in matches compare
|
|
* @param compare required reference value
|
|
* @return original value
|
|
* @warning T is bound by platform and compiler constraints
|
|
*/
|
|
static T CompareExchange(T *in, T replace, T compare);
|
|
|
|
/**
|
|
* @brief { return *in & (1 << offset); in |= (1 << offset) }
|
|
* @param in
|
|
* @param offset Bit index
|
|
* @return *in & (1 << offset)
|
|
* @warning T is bound by platform and compiler constraints
|
|
*/
|
|
static bool TestAndSet(T *in, const AuUInt8 offset);
|
|
};
|
|
|
|
#if defined(AURORA_COMPILER_MSVC)
|
|
|
|
template <>
|
|
inline auline AuUInt64 AuAtomicUtils<AuUInt64>::CompareExchange(AuUInt64 *in, AuUInt64 replace, AuUInt64 compare)
|
|
{
|
|
return static_cast<AuUInt64>(_InterlockedCompareExchange64(reinterpret_cast<long long volatile *>(in), static_cast<long long>(replace), static_cast<long long>(compare)));
|
|
}
|
|
|
|
template <>
|
|
inline auline AuUInt32 AuAtomicUtils<AuUInt32>::CompareExchange(AuUInt32 *in, AuUInt32 replace, AuUInt32 compare)
|
|
{
|
|
return static_cast<AuUInt32>(_InterlockedCompareExchange(reinterpret_cast<long volatile *>(in), static_cast<long>(replace), static_cast<long>(compare)));
|
|
}
|
|
|
|
template <>
|
|
inline auline AuUInt16 AuAtomicUtils<AuUInt16>::CompareExchange(AuUInt16 *in, AuUInt16 replace, AuUInt16 compare)
|
|
{
|
|
return static_cast<AuUInt16>(_InterlockedCompareExchange16(reinterpret_cast<short volatile *>(in), static_cast<short>(replace), static_cast<short>(compare)));
|
|
}
|
|
|
|
template <>
|
|
inline auline AuInt64 AuAtomicUtils<AuInt64>::CompareExchange(AuInt64 *in, AuInt64 replace, AuInt64 compare)
|
|
{
|
|
return _InterlockedCompareExchange64(reinterpret_cast<long long volatile *>(in), static_cast<long long>(replace), static_cast<long long>(compare));
|
|
}
|
|
|
|
template <>
|
|
inline auline AuInt32 AuAtomicUtils<AuInt32>::CompareExchange(AuInt32 *in, AuInt32 replace, AuInt32 compare)
|
|
{
|
|
return _InterlockedCompareExchange(reinterpret_cast<long volatile *>(in), static_cast<long>(replace), static_cast<long>(compare));
|
|
}
|
|
|
|
template <>
|
|
inline auline AuInt16 AuAtomicUtils<AuInt16>::CompareExchange(AuInt16 *in, AuInt16 replace, AuInt16 compare)
|
|
{
|
|
return _InterlockedCompareExchange16(reinterpret_cast<short volatile *>(in), static_cast<short>(replace), static_cast<short>(compare));
|
|
}
|
|
|
|
#if !defined(AURORA_IS_32BIT)
|
|
template <>
|
|
inline auline AuUInt64 AuAtomicUtils<AuUInt64>::Add(AuUInt64 *in, AuUInt64 addend)
|
|
{
|
|
return static_cast<AuUInt64>(_InterlockedExchangeAdd64(reinterpret_cast<long long volatile *>(in), static_cast<long long>(addend)) + static_cast<long long>(addend));
|
|
}
|
|
#endif
|
|
|
|
template <>
|
|
inline auline AuUInt32 AuAtomicUtils<AuUInt32>::Add(AuUInt32 *in, AuUInt32 addend)
|
|
{
|
|
return static_cast<AuUInt32>(_InterlockedExchangeAdd(reinterpret_cast<long volatile *>(in), static_cast<long>(addend)) + static_cast<long>(addend));
|
|
}
|
|
|
|
#if !defined(AURORA_IS_32BIT)
|
|
template <>
|
|
inline auline AuInt64 AuAtomicUtils<AuInt64>::Add(AuInt64 *in, AuInt64 addend)
|
|
{
|
|
return _InterlockedExchangeAdd64(reinterpret_cast<long long volatile *>(in), static_cast<long long>(addend)) + static_cast<long long>(addend);
|
|
}
|
|
#endif
|
|
|
|
template <>
|
|
inline auline AuInt32 AuAtomicUtils<AuInt32>::Add(AuInt32 *in, AuInt32 addend)
|
|
{
|
|
return _InterlockedExchangeAdd(reinterpret_cast<long volatile *>(in), static_cast<long>(addend)) + static_cast<long>(addend);
|
|
}
|
|
|
|
// TODO:
|
|
#if 0
|
|
template <>
|
|
inline auline AuUInt16 AuAtomicUtils<AuUInt16>::Add(AuUInt16 *in, AuUInt16 addend)
|
|
{
|
|
return {};
|
|
}
|
|
#endif
|
|
|
|
|
|
template <>
|
|
inline auline AuUInt64 AuAtomicUtils<AuUInt64>::Sub(AuUInt64 *in, AuUInt64 minuend)
|
|
{
|
|
return Add(in, AuUInt64(0) - minuend);
|
|
}
|
|
|
|
template <>
|
|
inline auline AuUInt32 AuAtomicUtils<AuUInt32>::Sub(AuUInt32 *in, AuUInt32 minuend)
|
|
{
|
|
return Add(in, AuUInt32(0) - minuend);
|
|
}
|
|
|
|
// TODO:
|
|
#if 0
|
|
template <>
|
|
inline auline AuUInt16 AuAtomicUtils<AuUInt16>::Sub(AuUInt16 *in, AuUInt16 minuend)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(AURORA_IS_32BIT)
|
|
template <>
|
|
inline auline AuUInt64 AuAtomicUtils<AuUInt64>::Set(AuUInt64 *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr64(reinterpret_cast<long long volatile *>(in), AuUInt64(1) << offset);
|
|
}
|
|
#endif
|
|
|
|
template <>
|
|
inline auline AuUInt32 AuAtomicUtils<AuUInt32>::Set(AuUInt32 *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr(reinterpret_cast<long volatile *>(in), 1 << offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline AuUInt16 AuAtomicUtils<AuUInt16>::Set(AuUInt16 *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr16(reinterpret_cast<short volatile *>(in), 1 << offset);
|
|
}
|
|
|
|
#if !defined(AURORA_IS_32BIT)
|
|
template <>
|
|
inline auline AuInt64 AuAtomicUtils<AuInt64>::Set(AuInt64 *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr64(reinterpret_cast<long long volatile *>(in), AuUInt64(1) << offset);
|
|
}
|
|
#endif
|
|
|
|
template <>
|
|
inline auline AuInt32 AuAtomicUtils<AuInt32>::Set(AuInt32 *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr(reinterpret_cast<long volatile *>(in), 1 << offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline long AuAtomicUtils<long>::Set(long *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr(reinterpret_cast<long volatile *>(in), 1 << offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline unsigned long AuAtomicUtils<unsigned long>::Set(unsigned long *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr(reinterpret_cast<long volatile *>(in), 1 << offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline AuInt16 AuAtomicUtils<AuInt16>::Set(AuInt16 *in, AuUInt8 offset)
|
|
{
|
|
return _InterlockedOr16(reinterpret_cast<short volatile *>(in), 1 << offset);
|
|
}
|
|
|
|
#elif defined(AURORA_COMPILER_CLANG) || defined(AURORA_COMPILER_GCC)
|
|
|
|
template <class T>
|
|
inline auline T AuAtomicUtils<T>::CompareExchange(T *in, T replace, T compare)
|
|
{
|
|
return __sync_val_compare_and_swap(in, compare, replace);
|
|
}
|
|
|
|
template <class T>
|
|
inline auline T AuAtomicUtils<T>::Add(T *in, T addend)
|
|
{
|
|
return __sync_add_and_fetch(in, addend);
|
|
}
|
|
|
|
template <class T>
|
|
inline auline T AuAtomicUtils<T>::Sub(T *in, T minuend)
|
|
{
|
|
return __sync_sub_and_fetch(in, minuend);
|
|
}
|
|
|
|
template <class T>
|
|
inline auline T AuAtomicUtils<T>::Set(T *in, AuUInt8 offset)
|
|
{
|
|
return __sync_fetch_and_or(in, T(1) << offset);
|
|
}
|
|
|
|
#endif
|
|
|
|
template <class T>
|
|
inline auline bool AuAtomicUtils<T>::TestAndSet(T *in, const AuUInt8 offset)
|
|
{
|
|
return AuAtomicUtils<T>::Set(in, offset) & (1 << offset);
|
|
}
|
|
|
|
#if defined(AURORA_COMPILER_MSVC) && (defined(AURORA_ARCH_X64) || defined(AURORA_ARCH_X86))
|
|
|
|
template <>
|
|
inline auline bool AuAtomicUtils<unsigned long>::TestAndSet(unsigned long *in, const AuUInt8 offset)
|
|
{
|
|
return _interlockedbittestandset(reinterpret_cast<volatile long *>(in), offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline bool AuAtomicUtils<long>::TestAndSet(long *in, const AuUInt8 offset)
|
|
{
|
|
return _interlockedbittestandset(reinterpret_cast<volatile long *>(in), offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline bool AuAtomicUtils<AuUInt32>::TestAndSet(AuUInt32 *in, const AuUInt8 offset)
|
|
{
|
|
return _interlockedbittestandset(reinterpret_cast<volatile long *>(in), offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline bool AuAtomicUtils<AuInt32>::TestAndSet(AuInt32 *in, const AuUInt8 offset)
|
|
{
|
|
return _interlockedbittestandset(reinterpret_cast<volatile long *>(in), offset);
|
|
}
|
|
|
|
#if !defined(AURORA_IS_32BIT)
|
|
template <>
|
|
inline auline bool AuAtomicUtils<AuUInt64>::TestAndSet(AuUInt64 *in, const AuUInt8 offset)
|
|
{
|
|
return _interlockedbittestandset64(reinterpret_cast<volatile long long *>(in), offset);
|
|
}
|
|
|
|
template <>
|
|
inline auline bool AuAtomicUtils<AuInt64>::TestAndSet(AuInt64 *in, const AuUInt8 offset)
|
|
{
|
|
return _interlockedbittestandset64(reinterpret_cast<volatile long long *>(in), offset);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
template <class T>
|
|
auline
|
|
T AuAtomicSet(T *in, AuUInt8 offset)
|
|
{
|
|
return AuAtomicUtils<T>::Set(in, offset);
|
|
}
|
|
|
|
template <class T>
|
|
auline
|
|
T AuAtomicAdd(T *in, T addend)
|
|
{
|
|
return AuAtomicUtils<T>::Add(in, addend);
|
|
}
|
|
|
|
template <class T>
|
|
auline
|
|
T AuAtomicSub(T *in, T minuend)
|
|
{
|
|
return AuAtomicUtils<T>::Sub(in, minuend);
|
|
}
|
|
|
|
template <class T>
|
|
auline
|
|
T AuAtomicCompareExchange(T *in, T replace, T compare)
|
|
{
|
|
return AuAtomicUtils<T>::CompareExchange(in, replace, compare);
|
|
}
|
|
|
|
template <class T>
|
|
auline
|
|
bool AuAtomicTestAndSet(T *in, AuUInt8 offset)
|
|
{
|
|
return AuAtomicUtils<T>::TestAndSet(in, offset);
|
|
} |