Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RHistUtils.hxx
Go to the documentation of this file.
1/// \file
2/// \warning This file contains implementation details that will change without notice. User code should never include
3/// this header directly.
4
5#ifndef ROOT_RHistUtils
6#define ROOT_RHistUtils
7
8#include <tuple>
9#include <type_traits>
10#include <utility>
11
12#ifdef _MSC_VER
13#include <atomic>
14#include <cstddef>
15
16#include <intrin.h>
17#endif
18
19namespace ROOT {
20namespace Experimental {
21namespace Internal {
22
23template <typename T, typename... Ts>
24struct LastType : LastType<Ts...> {};
25template <typename T>
26struct LastType<T> {
27 using type = T;
28};
29
30template <typename... Ts, std::size_t... I, typename A>
31std::tuple<const Ts &..., const A &>
32AppendReferenceImpl(const std::tuple<Ts...> &t, std::index_sequence<I...>, const A &a)
33{
34 return std::tuple<const Ts &..., const A &>(std::get<I>(t)..., a);
35}
36
37template <typename... Ts, typename A>
38std::tuple<const Ts &..., const A &> AppendReference(const std::tuple<Ts...> &t, const A &a)
39{
40 return AppendReferenceImpl(t, std::make_index_sequence<sizeof...(Ts)>(), a);
41}
42
43#ifdef _MSC_VER
44namespace MSVC {
45template <std::size_t N>
46struct AtomicOps {};
47
48template <>
49struct AtomicOps<1> {
50 static void Load(const void *ptr, void *ret)
51 {
52 *static_cast<char *>(ret) = __iso_volatile_load8(static_cast<const char *>(ptr));
53 }
54 static void Store(void *ptr, void *val)
55 {
56 __iso_volatile_store8(static_cast<char *>(ptr), *static_cast<char *>(val));
57 }
58 static void Add(void *ptr, const void *val)
59 {
60 _InterlockedExchangeAdd8(static_cast<char *>(ptr), *static_cast<const char *>(val));
61 }
62 static bool CompareExchange(void *ptr, void *expected, const void *desired)
63 {
64 // MSVC functions have the arguments reversed...
65 const char expectedVal = *static_cast<char *>(expected);
66 const char desiredVal = *static_cast<const char *>(desired);
67 const char previous = _InterlockedCompareExchange8(static_cast<char *>(ptr), desiredVal, expectedVal);
68 if (previous == expectedVal) {
69 return true;
70 }
71 *static_cast<char *>(expected) = previous;
72 return false;
73 }
74};
75
76template <>
77struct AtomicOps<2> {
78 static void Load(const void *ptr, void *ret)
79 {
80 *static_cast<short *>(ret) = __iso_volatile_load16(static_cast<const short *>(ptr));
81 }
82 static void Store(void *ptr, void *val)
83 {
84 __iso_volatile_store16(static_cast<short *>(ptr), *static_cast<short *>(val));
85 }
86 static void Add(void *ptr, const void *val)
87 {
88 _InterlockedExchangeAdd16(static_cast<short *>(ptr), *static_cast<const short *>(val));
89 }
90 static bool CompareExchange(void *ptr, void *expected, const void *desired)
91 {
92 // MSVC functions have the arguments reversed...
93 const short expectedVal = *static_cast<short *>(expected);
94 const short desiredVal = *static_cast<const short *>(desired);
95 const short previous = _InterlockedCompareExchange16(static_cast<short *>(ptr), desiredVal, expectedVal);
96 if (previous == expectedVal) {
97 return true;
98 }
99 *static_cast<short *>(expected) = previous;
100 return false;
101 }
102};
103
104template <>
105struct AtomicOps<4> {
106 static void Load(const void *ptr, void *ret)
107 {
108 *static_cast<int *>(ret) = __iso_volatile_load32(static_cast<const int *>(ptr));
109 }
110 static void Store(void *ptr, void *val)
111 {
112 __iso_volatile_store32(static_cast<int *>(ptr), *static_cast<int *>(val));
113 }
114 static void Add(void *ptr, const void *val)
115 {
116 _InterlockedExchangeAdd(static_cast<long *>(ptr), *static_cast<const long *>(val));
117 }
118 static bool CompareExchange(void *ptr, void *expected, const void *desired)
119 {
120 // MSVC functions have the arguments reversed...
121 const long expectedVal = *static_cast<long *>(expected);
122 const long desiredVal = *static_cast<const long *>(desired);
123 const long previous = _InterlockedCompareExchange(static_cast<long *>(ptr), desiredVal, expectedVal);
124 if (previous == expectedVal) {
125 return true;
126 }
127 *static_cast<long *>(expected) = previous;
128 return false;
129 }
130};
131
132template <>
133struct AtomicOps<8> {
134 static void Load(const void *ptr, void *ret)
135 {
136 *static_cast<__int64 *>(ret) = __iso_volatile_load64(static_cast<const __int64 *>(ptr));
137 }
138 static void Store(void *ptr, void *val)
139 {
140 __iso_volatile_store64(static_cast<__int64 *>(ptr), *static_cast<__int64 *>(val));
141 }
142 static void Add(void *ptr, const void *val);
143 static bool CompareExchange(void *ptr, void *expected, const void *desired)
144 {
145 // MSVC functions have the arguments reversed...
146 const __int64 expectedVal = *static_cast<__int64 *>(expected);
147 const __int64 desiredVal = *static_cast<const __int64 *>(desired);
149 if (previous == expectedVal) {
150 return true;
151 }
152 *static_cast<__int64 *>(expected) = previous;
153 return false;
154 }
155};
156} // namespace MSVC
157#endif
158
159template <typename T>
160std::enable_if_t<std::is_arithmetic_v<T>> AtomicLoad(const T *ptr, T *ret)
161{
162#ifndef _MSC_VER
164#else
165 MSVC::AtomicOps<sizeof(T)>::Load(ptr, ret);
166#endif
167}
168
169template <typename T>
170auto AtomicLoad(const T *ptr, T *ret) -> decltype(ptr->AtomicLoad(ret))
171{
172 return ptr->AtomicLoad(ret);
173}
174
175template <typename T>
176std::enable_if_t<std::is_arithmetic_v<T>> AtomicLoadAcquire(const T *ptr, T *ret)
177{
178#ifndef _MSC_VER
180#else
181 MSVC::AtomicOps<sizeof(T)>::Load(ptr, ret);
182 // Cannot specify the memory order directly, use a fence.
183 std::atomic_thread_fence(std::memory_order_acquire);
184#endif
185}
186
187template <typename T>
188std::enable_if_t<std::is_arithmetic_v<T>> AtomicStoreRelease(T *ptr, T *val)
189{
190#ifndef _MSC_VER
192#else
193 // Cannot specify the memory order directly, use a fence.
194 std::atomic_thread_fence(std::memory_order_release);
195 MSVC::AtomicOps<sizeof(T)>::Store(ptr, val);
196#endif
197}
198
199template <typename T>
200std::enable_if_t<std::is_arithmetic_v<T>, bool> AtomicCompareExchange(T *ptr, T *expected, T *desired)
201{
202#ifndef _MSC_VER
204#else
205 return MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
206#endif
207}
208
209template <typename T>
210std::enable_if_t<std::is_arithmetic_v<T>, bool> AtomicCompareExchangeAcquire(T *ptr, T *expected, T *desired)
211{
212#ifndef _MSC_VER
214#else
215 bool success = MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
216 // Cannot specify the memory order directly, use an unconditional fence to avoid branching code.
217 std::atomic_thread_fence(std::memory_order_acquire);
218 return success;
219#endif
220}
221
222template <typename T>
223std::enable_if_t<std::is_arithmetic_v<T>, bool> AtomicCompareExchangeRelease(T *ptr, T *expected, T *desired)
224{
225#ifndef _MSC_VER
227#else
228 // Cannot specify the memory order directly, use an unconditional fence to avoid branching code.
229 std::atomic_thread_fence(std::memory_order_release);
230 return MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
231#endif
232}
233
234template <typename T>
235std::enable_if_t<std::is_arithmetic_v<T>> AtomicAddCompareExchangeLoop(T *ptr, T val)
236{
237 T expected;
238 AtomicLoad(ptr, &expected);
239 T desired = expected + val;
240 while (!AtomicCompareExchange(ptr, &expected, &desired)) {
241 // expected holds the new value; try again.
242 desired = expected + val;
243 }
244}
245
246template <typename T>
247std::enable_if_t<std::is_arithmetic_v<T>> AtomicAddCompareExchangeReleaseLoop(T *ptr, T val)
248{
249 T expected;
250 AtomicLoad(ptr, &expected);
251 T desired = expected + val;
253 // expected holds the new value; try again.
254 desired = expected + val;
255 }
256}
257
258#ifdef _MSC_VER
259namespace MSVC {
260inline void AtomicOps<8>::Add(void *ptr, const void *val)
261{
262#if _WIN64
263 _InterlockedExchangeAdd64(static_cast<__int64 *>(ptr), *static_cast<const __int64 *>(val));
264#else
265 AtomicAddCompareExchangeLoop(static_cast<__int64 *>(ptr), *static_cast<const __int64 *>(val));
266#endif
267}
268} // namespace MSVC
269#endif
270
271template <typename T>
272std::enable_if_t<std::is_integral_v<T>> AtomicAdd(T *ptr, T val)
273{
274#ifndef _MSC_VER
276#else
277 MSVC::AtomicOps<sizeof(T)>::Add(ptr, &val);
278#endif
279}
280
281template <typename T>
282std::enable_if_t<std::is_floating_point_v<T>> AtomicAdd(T *ptr, T val)
283{
285}
286
287template <typename T>
288std::enable_if_t<std::is_integral_v<T>> AtomicAddRelease(T *ptr, T val)
289{
290#ifndef _MSC_VER
292#else
293 // Cannot specify the memory order directly, use a fence.
294 std::atomic_thread_fence(std::memory_order_release);
295 MSVC::AtomicOps<sizeof(T)>::Add(ptr, &val);
296#endif
297}
298
299template <typename T>
300std::enable_if_t<std::is_floating_point_v<T>> AtomicAddRelease(T *ptr, T val)
301{
303}
304
305// For adding a double-precision weight to a single-precision bin content type, cast the argument once before the
306// compare-exchange loop.
307static inline void AtomicAddRelease(float *ptr, double val)
308{
309 AtomicAddRelease(ptr, static_cast<float>(val));
310}
311
312template <typename T>
313std::enable_if_t<std::is_arithmetic_v<T>> AtomicIncRelease(T *ptr)
314{
315 AtomicAddRelease(ptr, static_cast<T>(1));
316}
317
318template <typename T, typename U>
319auto AtomicAdd(T *ptr, const U &add) -> decltype(ptr->AtomicAdd(add))
320{
321 return ptr->AtomicAdd(add);
322}
323
324template <typename T, typename U>
325auto AtomicAddRelease(T *ptr, const U &add) -> decltype(ptr->AtomicAddRelease(add))
326{
327 return ptr->AtomicAddRelease(add);
328}
329
330template <typename T>
331auto AtomicIncRelease(T *ptr) -> decltype(ptr->AtomicIncRelease())
332{
333 return ptr->AtomicIncRelease();
334}
335
336} // namespace Internal
337} // namespace Experimental
338} // namespace ROOT
339
340#endif
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define I(x, y, z)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicIncRelease(T *ptr)
std::enable_if_t< std::is_arithmetic_v< T >, bool > AtomicCompareExchangeRelease(T *ptr, T *expected, T *desired)
std::enable_if_t< std::is_arithmetic_v< T >, bool > AtomicCompareExchange(T *ptr, T *expected, T *desired)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicAddCompareExchangeLoop(T *ptr, T val)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicAddCompareExchangeReleaseLoop(T *ptr, T val)
std::tuple< const Ts &..., const A & > AppendReference(const std::tuple< Ts... > &t, const A &a)
std::enable_if_t< std::is_arithmetic_v< T >, bool > AtomicCompareExchangeAcquire(T *ptr, T *expected, T *desired)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicLoadAcquire(const T *ptr, T *ret)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicStoreRelease(T *ptr, T *val)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicLoad(const T *ptr, T *ret)
std::tuple< const Ts &..., const A & > AppendReferenceImpl(const std::tuple< Ts... > &t, std::index_sequence< I... >, const A &a)
std::enable_if_t< std::is_integral_v< T > > AtomicAdd(T *ptr, T val)
std::enable_if_t< std::is_integral_v< T > > AtomicAddRelease(T *ptr, T val)
TMatrixT< Element > & Add(TMatrixT< Element > &target, Element scalar, const TMatrixT< Element > &source)
Modify addition: target += scalar * source.