55#ifndef ROOT_RHistUtils
66#define ROOT_RHistUtils
77
8+ #include < type_traits>
9+
10+ #ifdef _MSC_VER
11+ #include < intrin.h>
12+ #endif
13+
814namespace ROOT {
915namespace Experimental {
1016namespace Internal {
@@ -16,6 +22,180 @@ struct LastType<T> {
1622 using type = T;
1723};
1824
25+ #ifdef _MSC_VER
26+ namespace MSVC {
27+ template <std::size_t N>
28+ struct AtomicOps {};
29+
30+ template <>
31+ struct AtomicOps <1 > {
32+ static void Load (const void *ptr, void *ret)
33+ {
34+ *static_cast <char *>(ret) = __iso_volatile_load8 (static_cast <const char *>(ptr));
35+ }
36+ static void Add (void *ptr, const void *val)
37+ {
38+ _InterlockedExchangeAdd8 (static_cast <char *>(ptr), *static_cast <const char *>(val));
39+ }
40+ static bool CompareExchange (void *ptr, void *expected, const void *desired)
41+ {
42+ // MSVC functions have the arguments reversed...
43+ char expectedVal = *static_cast <char *>(expected);
44+ char previous =
45+ _InterlockedCompareExchange8 (static_cast <char *>(ptr), *static_cast <const char *>(desired), expectedVal);
46+ if (previous == expectedVal) {
47+ return true ;
48+ }
49+ *static_cast <char *>(expected) = previous;
50+ return false ;
51+ }
52+ };
53+
54+ template <>
55+ struct AtomicOps <2 > {
56+ static void Load (const void *ptr, void *ret)
57+ {
58+ *static_cast <short *>(ret) = __iso_volatile_load16 (static_cast <const short *>(ptr));
59+ }
60+ static void Add (void *ptr, const void *val)
61+ {
62+ _InterlockedExchangeAdd16 (static_cast <short *>(ptr), *static_cast <const short *>(val));
63+ }
64+ static bool CompareExchange (void *ptr, void *expected, const void *desired)
65+ {
66+ // MSVC functions have the arguments reversed...
67+ short expectedVal = *static_cast <short *>(expected);
68+ short previous =
69+ _InterlockedCompareExchange16 (static_cast <short *>(ptr), *static_cast <const short *>(desired), expectedVal);
70+ if (previous == expectedVal) {
71+ return true ;
72+ }
73+ *static_cast <short *>(expected) = previous;
74+ return false ;
75+ }
76+ };
77+
78+ template <>
79+ struct AtomicOps <4 > {
80+ static void Load (const void *ptr, void *ret)
81+ {
82+ *static_cast <int *>(ret) = __iso_volatile_load32 (static_cast <const int *>(ptr));
83+ }
84+ static void Add (void *ptr, const void *val)
85+ {
86+ _InterlockedExchangeAdd (static_cast <long *>(ptr), *static_cast <const long *>(val));
87+ }
88+ static bool CompareExchange (void *ptr, void *expected, const void *desired)
89+ {
90+ // MSVC functions have the arguments reversed...
91+ long expectedVal = *static_cast <long *>(expected);
92+ long previous =
93+ _InterlockedCompareExchange (static_cast <long *>(ptr), *static_cast <const long *>(desired), expectedVal);
94+ if (previous == expectedVal) {
95+ return true ;
96+ }
97+ *static_cast <long *>(expected) = previous;
98+ return false ;
99+ }
100+ };
101+
102+ template <>
103+ struct AtomicOps <8 > {
104+ static void Load (const void *ptr, void *ret)
105+ {
106+ *static_cast <__int64 *>(ret) = __iso_volatile_load64 (static_cast <const __int64 *>(ptr));
107+ }
108+ static void Add (void *ptr, const void *val);
109+ static bool CompareExchange (void *ptr, void *expected, const void *desired)
110+ {
111+ // MSVC functions have the arguments reversed...
112+ __int64 expectedVal = *static_cast <__int64 *>(expected);
113+ __int64 previous = _InterlockedCompareExchange64 (static_cast <__int64 *>(ptr),
114+ *static_cast <const __int64 *>(desired), expectedVal);
115+ if (previous == expectedVal) {
116+ return true ;
117+ }
118+ *static_cast <__int64 *>(expected) = previous;
119+ return false ;
120+ }
121+ };
122+ } // namespace MSVC
123+ #endif
124+
125+ template <typename T>
126+ void AtomicLoad (const T *ptr, T *ret)
127+ {
128+ #ifndef _MSC_VER
129+ __atomic_load (ptr, ret, __ATOMIC_RELAXED);
130+ #else
131+ MSVC::AtomicOps<sizeof (T)>::Load (ptr, ret);
132+ #endif
133+ }
134+
135+ template <typename T>
136+ bool AtomicCompareExchange (T *ptr, T *expected, T *desired)
137+ {
138+ #ifndef _MSC_VER
139+ return __atomic_compare_exchange (ptr, expected, desired, /* weak=*/ false , __ATOMIC_RELAXED, __ATOMIC_RELAXED);
140+ #else
141+ return MSVC::AtomicOps<sizeof (T)>::CompareExchange (ptr, expected, desired);
142+ #endif
143+ }
144+
145+ template <typename T>
146+ void AtomicAddCompareExchangeLoop (T *ptr, T val)
147+ {
148+ T expected;
149+ AtomicLoad (ptr, &expected);
150+ T desired = expected + val;
151+ while (!AtomicCompareExchange (ptr, &expected, &desired)) {
152+ // expected holds the new value; try again.
153+ desired = expected + val;
154+ }
155+ }
156+
157+ #ifdef _MSC_VER
158+ namespace MSVC {
159+ inline void AtomicOps<8 >::Add(void *ptr, const void *val)
160+ {
161+ #if _WIN64
162+ _InterlockedExchangeAdd64 (static_cast <__int64 *>(ptr), *static_cast <const __int64 *>(val));
163+ #else
164+ AtomicAddCompareExchangeLoop (static_cast <__int64 *>(ptr), *static_cast <const __int64 *>(val));
165+ #endif
166+ }
167+ } // namespace MSVC
168+ #endif
169+
170+ template <typename T>
171+ std::enable_if_t <std::is_integral_v<T>> AtomicAdd (T *ptr, T val)
172+ {
173+ #ifndef _MSC_VER
174+ __atomic_fetch_add (ptr, val, __ATOMIC_RELAXED);
175+ #else
176+ MSVC::AtomicOps<sizeof (T)>::Add (ptr, &val);
177+ #endif
178+ }
179+
180+ template <typename T>
181+ std::enable_if_t <std::is_floating_point_v<T>> AtomicAdd (T *ptr, T val)
182+ {
183+ AtomicAddCompareExchangeLoop (ptr, val);
184+ }
185+
186+ // For adding a double-precision weight to a single-precision bin content type, cast the argument once before the
187+ // compare-exchange loop.
188+ static inline void AtomicAdd (float *ptr, double val)
189+ {
190+ AtomicAdd (ptr, static_cast <float >(val));
191+ }
192+
193+ template <typename T>
194+ std::enable_if_t <std::is_arithmetic_v<T>> AtomicInc (T *ptr)
195+ {
196+ AtomicAdd (ptr, static_cast <T>(1 ));
197+ }
198+
19199} // namespace Internal
20200} // namespace Experimental
21201} // namespace ROOT
0 commit comments