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 <type_traits>
9
10#ifdef _MSC_VER
11#include <atomic>
12#include <cstddef>
13
14#include <intrin.h>
15#endif
16
17namespace ROOT {
18namespace Experimental {
19namespace Internal {
20
21template <typename T, typename... Ts>
22struct LastType : LastType<Ts...> {};
23template <typename T>
24struct LastType<T> {
25 using type = T;
26};
27
28#ifdef _MSC_VER
29namespace MSVC {
30template <std::size_t N>
31struct AtomicOps {};
32
33template <>
34struct AtomicOps<1> {
35 static void Load(const void *ptr, void *ret)
36 {
37 *static_cast<char *>(ret) = __iso_volatile_load8(static_cast<const char *>(ptr));
38 }
39 static void Store(void *ptr, void *val)
40 {
41 __iso_volatile_store8(static_cast<char *>(ptr), *static_cast<char *>(val));
42 }
43 static void Add(void *ptr, const void *val)
44 {
45 _InterlockedExchangeAdd8(static_cast<char *>(ptr), *static_cast<const char *>(val));
46 }
47 static bool CompareExchange(void *ptr, void *expected, const void *desired)
48 {
49 // MSVC functions have the arguments reversed...
50 const char expectedVal = *static_cast<char *>(expected);
51 const char desiredVal = *static_cast<const char *>(desired);
52 const char previous = _InterlockedCompareExchange8(static_cast<char *>(ptr), desiredVal, expectedVal);
53 if (previous == expectedVal) {
54 return true;
55 }
56 *static_cast<char *>(expected) = previous;
57 return false;
58 }
59};
60
61template <>
62struct AtomicOps<2> {
63 static void Load(const void *ptr, void *ret)
64 {
65 *static_cast<short *>(ret) = __iso_volatile_load16(static_cast<const short *>(ptr));
66 }
67 static void Store(void *ptr, void *val)
68 {
69 __iso_volatile_store16(static_cast<short *>(ptr), *static_cast<short *>(val));
70 }
71 static void Add(void *ptr, const void *val)
72 {
73 _InterlockedExchangeAdd16(static_cast<short *>(ptr), *static_cast<const short *>(val));
74 }
75 static bool CompareExchange(void *ptr, void *expected, const void *desired)
76 {
77 // MSVC functions have the arguments reversed...
78 const short expectedVal = *static_cast<short *>(expected);
79 const short desiredVal = *static_cast<const short *>(desired);
80 const short previous = _InterlockedCompareExchange16(static_cast<short *>(ptr), desiredVal, expectedVal);
81 if (previous == expectedVal) {
82 return true;
83 }
84 *static_cast<short *>(expected) = previous;
85 return false;
86 }
87};
88
89template <>
90struct AtomicOps<4> {
91 static void Load(const void *ptr, void *ret)
92 {
93 *static_cast<int *>(ret) = __iso_volatile_load32(static_cast<const int *>(ptr));
94 }
95 static void Store(void *ptr, void *val)
96 {
97 __iso_volatile_store32(static_cast<int *>(ptr), *static_cast<int *>(val));
98 }
99 static void Add(void *ptr, const void *val)
100 {
101 _InterlockedExchangeAdd(static_cast<long *>(ptr), *static_cast<const long *>(val));
102 }
103 static bool CompareExchange(void *ptr, void *expected, const void *desired)
104 {
105 // MSVC functions have the arguments reversed...
106 const long expectedVal = *static_cast<long *>(expected);
107 const long desiredVal = *static_cast<const long *>(desired);
108 const long previous = _InterlockedCompareExchange(static_cast<long *>(ptr), desiredVal, expectedVal);
109 if (previous == expectedVal) {
110 return true;
111 }
112 *static_cast<long *>(expected) = previous;
113 return false;
114 }
115};
116
117template <>
118struct AtomicOps<8> {
119 static void Load(const void *ptr, void *ret)
120 {
121 *static_cast<__int64 *>(ret) = __iso_volatile_load64(static_cast<const __int64 *>(ptr));
122 }
123 static void Store(void *ptr, void *val)
124 {
125 __iso_volatile_store64(static_cast<__int64 *>(ptr), *static_cast<__int64 *>(val));
126 }
127 static void Add(void *ptr, const void *val);
128 static bool CompareExchange(void *ptr, void *expected, const void *desired)
129 {
130 // MSVC functions have the arguments reversed...
131 const __int64 expectedVal = *static_cast<__int64 *>(expected);
132 const __int64 desiredVal = *static_cast<const __int64 *>(desired);
134 if (previous == expectedVal) {
135 return true;
136 }
137 *static_cast<__int64 *>(expected) = previous;
138 return false;
139 }
140};
141} // namespace MSVC
142#endif
143
144template <typename T>
145void AtomicLoad(const T *ptr, T *ret)
146{
147#ifndef _MSC_VER
149#else
150 MSVC::AtomicOps<sizeof(T)>::Load(ptr, ret);
151#endif
152}
153
154template <typename T>
155void AtomicStoreRelease(T *ptr, T *val)
156{
157#ifndef _MSC_VER
159#else
160 // Cannot specify the memory order directly, use a fence.
161 std::atomic_thread_fence(std::memory_order_release);
162 MSVC::AtomicOps<sizeof(T)>::Store(ptr, val);
163#endif
164}
165
166template <typename T>
168{
169#ifndef _MSC_VER
171#else
172 return MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
173#endif
174}
175
176template <typename T>
178{
179#ifndef _MSC_VER
181#else
182 bool success = MSVC::AtomicOps<sizeof(T)>::CompareExchange(ptr, expected, desired);
183 // Cannot specify the memory order directly, use an unconditional fence to avoid branching code.
184 std::atomic_thread_fence(std::memory_order_acquire);
185 return success;
186#endif
187}
188
189template <typename T>
191{
192 T expected;
193 AtomicLoad(ptr, &expected);
194 T desired = expected + val;
195 while (!AtomicCompareExchange(ptr, &expected, &desired)) {
196 // expected holds the new value; try again.
197 desired = expected + val;
198 }
199}
200
201#ifdef _MSC_VER
202namespace MSVC {
203inline void AtomicOps<8>::Add(void *ptr, const void *val)
204{
205#if _WIN64
206 _InterlockedExchangeAdd64(static_cast<__int64 *>(ptr), *static_cast<const __int64 *>(val));
207#else
208 AtomicAddCompareExchangeLoop(static_cast<__int64 *>(ptr), *static_cast<const __int64 *>(val));
209#endif
210}
211} // namespace MSVC
212#endif
213
214template <typename T>
215std::enable_if_t<std::is_integral_v<T>> AtomicAdd(T *ptr, T val)
216{
217#ifndef _MSC_VER
219#else
220 MSVC::AtomicOps<sizeof(T)>::Add(ptr, &val);
221#endif
222}
223
224template <typename T>
225std::enable_if_t<std::is_floating_point_v<T>> AtomicAdd(T *ptr, T val)
226{
228}
229
230// For adding a double-precision weight to a single-precision bin content type, cast the argument once before the
231// compare-exchange loop.
232static inline void AtomicAdd(float *ptr, double val)
233{
234 AtomicAdd(ptr, static_cast<float>(val));
235}
236
237template <typename T>
238std::enable_if_t<std::is_arithmetic_v<T>> AtomicInc(T *ptr)
239{
240 AtomicAdd(ptr, static_cast<T>(1));
241}
242
243template <typename T, typename U>
244auto AtomicAdd(T *ptr, const U &add) -> decltype(ptr->AtomicAdd(add))
245{
246 return ptr->AtomicAdd(add);
247}
248
249template <typename T>
250auto AtomicInc(T *ptr) -> decltype(ptr->AtomicInc())
251{
252 return ptr->AtomicInc();
253}
254
255} // namespace Internal
256} // namespace Experimental
257} // namespace ROOT
258
259#endif
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
std::enable_if_t< std::is_arithmetic_v< T > > AtomicInc(T *ptr)
void AtomicLoad(const T *ptr, T *ret)
void AtomicStoreRelease(T *ptr, T *val)
bool AtomicCompareExchange(T *ptr, T *expected, T *desired)
bool AtomicCompareExchangeAcquire(T *ptr, T *expected, T *desired)
void AtomicAddCompareExchangeLoop(T *ptr, T val)
std::enable_if_t< std::is_integral_v< T > > AtomicAdd(T *ptr, T val)
TMatrixT< Element > & Add(TMatrixT< Element > &target, Element scalar, const TMatrixT< Element > &source)
Modify addition: target += scalar * source.