File indexing completed on 2026-11-02 10:33:58
0001
0002
0003
0004
0005 #ifndef ROOT_RHistUtils
0006 #define ROOT_RHistUtils
0007
0008 #include <atomic>
0009 #include <type_traits>
0010
0011 #ifdef _MSC_VER
0012 #include <intrin.h>
0013 #endif
0014
0015 namespace ROOT {
0016 namespace Experimental {
0017 namespace Internal {
0018
0019 template <typename T, typename... Ts>
0020 struct LastType : LastType<Ts...> {};
0021 template <typename T>
0022 struct LastType<T> {
0023 using type = T;
0024 };
0025
0026 #ifdef _MSC_VER
0027 namespace MSVC {
0028 template <std::size_t N>
0029 struct AtomicOps {};
0030
0031 template <>
0032 struct AtomicOps<1> {
0033 static void Load(const void *ptr, void *ret)
0034 {
0035 *static_cast<char *>(ret) = __iso_volatile_load8(static_cast<const char *>(ptr));
0036 }
0037 static void Store(void *ptr, void *val)
0038 {
0039 __iso_volatile_store8(static_cast<char *>(ptr), *static_cast<char *>(val));
0040 }
0041 static void Add(void *ptr, const void *val)
0042 {
0043 _InterlockedExchangeAdd8(static_cast<char *>(ptr), *static_cast<const char *>(val));
0044 }
0045 static bool CompareExchange(void *ptr, void *expected, const void *desired)
0046 {
0047
0048 const char expectedVal = *static_cast<char *>(expected);
0049 const char desiredVal = *static_cast<const char *>(desired);
0050 const char previous = _InterlockedCompareExchange8(static_cast<char *>(ptr), desiredVal, expectedVal);
0051 if (previous == expectedVal) {
0052 return true;
0053 }
0054 *static_cast<char *>(expected) = previous;
0055 return false;
0056 }
0057 };
0058
0059 template <>
0060 struct AtomicOps<2> {
0061 static void Load(const void *ptr, void *ret)
0062 {
0063 *static_cast<short *>(ret) = __iso_volatile_load16(static_cast<const short *>(ptr));
0064 }
0065 static void Store(void *ptr, void *val)
0066 {
0067 __iso_volatile_store16(static_cast<short *>(ptr), *static_cast<short *>(val));
0068 }
0069 static void Add(void *ptr, const void *val)
0070 {
0071 _InterlockedExchangeAdd16(static_cast<short *>(ptr), *static_cast<const short *>(val));
0072 }
0073 static bool CompareExchange(void *ptr, void *expected, const void *desired)
0074 {
0075
0076 const short expectedVal = *static_cast<short *>(expected);
0077 const short desiredVal = *static_cast<const short *>(desired);
0078 const short previous = _InterlockedCompareExchange16(static_cast<short *>(ptr), desiredVal, expectedVal);
0079 if (previous == expectedVal) {
0080 return true;
0081 }
0082 *static_cast<short *>(expected) = previous;
0083 return false;
0084 }
0085 };
0086
0087 template <>
0088 struct AtomicOps<4> {
0089 static void Load(const void *ptr, void *ret)
0090 {
0091 *static_cast<int *>(ret) = __iso_volatile_load32(static_cast<const int *>(ptr));
0092 }
0093 static void Store(void *ptr, void *val)
0094 {
0095 __iso_volatile_store32(static_cast<int *>(ptr), *static_cast<int *>(val));
0096 }
0097 static void Add(void *ptr, const void *val)
0098 {
0099 _InterlockedExchangeAdd(static_cast<long *>(ptr), *static_cast<const long *>(val));
0100 }
0101 static bool CompareExchange(void *ptr, void *expected, const void *desired)
0102 {
0103
0104 const long expectedVal = *static_cast<long *>(expected);
0105 const long desiredVal = *static_cast<const long *>(desired);
0106 const long previous = _InterlockedCompareExchange(static_cast<long *>(ptr), desiredVal, expectedVal);
0107 if (previous == expectedVal) {
0108 return true;
0109 }
0110 *static_cast<long *>(expected) = previous;
0111 return false;
0112 }
0113 };
0114
0115 template <>
0116 struct AtomicOps<8> {
0117 static void Load(const void *ptr, void *ret)
0118 {
0119 *static_cast<__int64 *>(ret) = __iso_volatile_load64(static_cast<const __int64 *>(ptr));
0120 }
0121 static void Store(void *ptr, void *val)
0122 {
0123 __iso_volatile_store64(static_cast<__int64 *>(ptr), *static_cast<__int64 *>(val));
0124 }
0125 static void Add(void *ptr, const void *val);
0126 static bool CompareExchange(void *ptr, void *expected, const void *desired)
0127 {
0128
0129 const __int64 expectedVal = *static_cast<__int64 *>(expected);
0130 const __int64 desiredVal = *static_cast<const __int64 *>(desired);
0131 const __int64 previous = _InterlockedCompareExchange64(static_cast<__int64 *>(ptr), desiredVal, expectedVal);
0132 if (previous == expectedVal) {
0133 return true;
0134 }
0135 *static_cast<__int64 *>(expected) = previous;
0136 return false;
0137 }
0138 };
0139 }
0140 #endif
0141
0142 template <typename T>
0143 void AtomicLoad(const T *ptr, T *ret)
0144 {
0145 #ifndef _MSC_VER
0146 __atomic_load(ptr, ret, __ATOMIC_RELAXED);
0147 #else
0148 MSVC::AtomicOps<sizeof(T)>::Load(ptr, ret);
0149 #endif
0150 }
0151
0152 template <typename T>
0153 void AtomicStoreRelease(T *ptr, T *val)
0154 {
0155 #ifndef _MSC_VER
0156 __atomic_store(ptr, val, __ATOMIC_RELEASE);
0157 #else
0158
0159 std::atomic_thread_fence(std::memory_order_release);
0160 MSVC::AtomicOps<sizeof(T)>::Store(ptr, val);
0161 #endif
0162 }
0163
0164 template <typename T>
0165 bool AtomicCompareExchange(T *ptr, T *expected, T *desired)
0166 {
0167 #ifndef _MSC_VER
0168 return __atomic_compare_exchange(ptr, expected, desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
0169 #else
0170 return MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
0171 #endif
0172 }
0173
0174 template <typename T>
0175 bool AtomicCompareExchangeAcquire(T *ptr, T *expected, T *desired)
0176 {
0177 #ifndef _MSC_VER
0178 return __atomic_compare_exchange(ptr, expected, desired, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
0179 #else
0180 bool success = MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
0181
0182 std::atomic_thread_fence(std::memory_order_acquire);
0183 return success;
0184 #endif
0185 }
0186
0187 template <typename T>
0188 void AtomicAddCompareExchangeLoop(T *ptr, T val)
0189 {
0190 T expected;
0191 AtomicLoad(ptr, &expected);
0192 T desired = expected + val;
0193 while (!AtomicCompareExchange(ptr, &expected, &desired)) {
0194
0195 desired = expected + val;
0196 }
0197 }
0198
0199 #ifdef _MSC_VER
0200 namespace MSVC {
0201 inline void AtomicOps<8>::Add(void *ptr, const void *val)
0202 {
0203 #if _WIN64
0204 _InterlockedExchangeAdd64(static_cast<__int64 *>(ptr), *static_cast<const __int64 *>(val));
0205 #else
0206 AtomicAddCompareExchangeLoop(static_cast<__int64 *>(ptr), *static_cast<const __int64 *>(val));
0207 #endif
0208 }
0209 }
0210 #endif
0211
0212 template <typename T>
0213 std::enable_if_t<std::is_integral_v<T>> AtomicAdd(T *ptr, T val)
0214 {
0215 #ifndef _MSC_VER
0216 __atomic_fetch_add(ptr, val, __ATOMIC_RELAXED);
0217 #else
0218 MSVC::AtomicOps<sizeof(T)>::Add(ptr, &val);
0219 #endif
0220 }
0221
0222 template <typename T>
0223 std::enable_if_t<std::is_floating_point_v<T>> AtomicAdd(T *ptr, T val)
0224 {
0225 AtomicAddCompareExchangeLoop(ptr, val);
0226 }
0227
0228
0229
0230 static inline void AtomicAdd(float *ptr, double val)
0231 {
0232 AtomicAdd(ptr, static_cast<float>(val));
0233 }
0234
0235 template <typename T>
0236 std::enable_if_t<std::is_arithmetic_v<T>> AtomicInc(T *ptr)
0237 {
0238 AtomicAdd(ptr, static_cast<T>(1));
0239 }
0240
0241 template <typename T, typename U>
0242 auto AtomicAdd(T *ptr, const U &add) -> decltype(ptr->AtomicAdd(add))
0243 {
0244 return ptr->AtomicAdd(add);
0245 }
0246
0247 template <typename T>
0248 auto AtomicInc(T *ptr) -> decltype(ptr->AtomicInc())
0249 {
0250 return ptr->AtomicInc();
0251 }
0252
0253 }
0254 }
0255 }
0256
0257 #endif