HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_ThreadSpecificValue.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * COMMENTS:
7  * This class defines a thread-specific pointer that can be used to
8  * implement thread-local storage. Each thread that accesses the
9  * pointer will have its own copy of the pointer's value, and the
10  * initial value for all threads is null.
11  *
12  * To replace a global variable like
13  *
14  * static int theGlobalValue = 0;
15  * // use theGlobalValue
16  *
17  * to use thread-local storage, you would write something like
18  *
19  * static UT_ThreadSpecificValue<int> theGlobalValue;
20  * // use theGlobalValue.get()
21  *
22  * Note that the default constructor for the values will be called.
23  * (For native data types like ints, floats, pointers, etc., they will
24  * be initialized to zero/null.)
25  *
26  * This class has been tuned for speed. Speed of TLS is surprisingly
27  * sensitive to changes to this class.
28  *
29  * RELATION TO THE STL:
30  *
31  * Use UT_ThreadSpecificValue instead of thread_local.
32  *
33  * Reasoning:
34  *
35  * Support for dynamic allocation across DLLs, IIRC
36  */
37 
38 #ifndef __UT_ThreadSpecificValue_h__
39 #define __UT_ThreadSpecificValue_h__
40 
41 #include "UT_Assert.h"
42 #include "UT_ConcurrentHashMap.h"
43 #include "UT_UniquePtr.h"
44 #include <SYS/SYS_Align.h>
46 #include <SYS/SYS_StaticAssert.h>
47 #include <SYS/SYS_Types.h>
48 #include <SYS/SYS_TypeTraits.h>
49 
50 #include <type_traits>
51 #include <utility>
52 #include <string.h>
53 
54 
55 #ifdef HAS_NO_TLS
56 static const size_t theNbStaticThreadValues = 1;
57 #else
58 static const size_t theNbStaticThreadValues = 32;
59 #endif
60 
61 namespace UT_ThreadSpecificValueDetail
62 {
63  template<typename U, size_t Align, typename Enable = void>
64  class AlignType;
65 
66  template<typename U, size_t Align>
67  class alignas(Align) AlignType<U, Align, std::enable_if_t<(Align > 0)>>
68  {
69  public:
71  {
72  // If T is an object, the default constructor will have been
73  // called. Otherwise, initialize the memory to 0.
74  if (SYSisPOD<U>())
75  memset((void *)&myValue, 0, sizeof(U)); // NOLINT
76  }
77 
78  operator const U &() const
79  {
80  return myValue;
81  }
82  operator U &()
83  {
84  return myValue;
85  }
86 
87  U &operator=(const U &v)
88  {
89  if (&v != &myValue)
90  myValue = v;
91  return myValue;
92  }
93 
94  private:
95  U myValue;
96  };
97 
98  template<typename U, size_t Align>
99  class AlignType<U, Align, std::enable_if_t<(Align == 0)>>
100  {
101  public:
103  {
104  // If T is an object, the default constructor will have been
105  // called. Otherwise, initialize the memory to 0.
106  if (SYSisPOD<U>())
107  memset((void *)&myValue, 0, sizeof(U));
108  }
109 
110  operator const U &() const
111  {
112  return myValue;
113  }
114  operator U &()
115  {
116  return myValue;
117  }
118 
119  U &operator=(const U &v)
120  {
121  if (&v != &myValue)
122  myValue = v;
123  return myValue;
124  }
125 
126  private:
127  U myValue;
128  };
129 }
130 
131 /// A class for storing thread local values of type T. By default, the
132 /// alignment of the type T's will be 64-byte aligned but this can be adjusted
133 /// by supplying the ALIGNMENT template parameter, or use 0 to disable. For
134 /// optimal performance, use an ALIGNMENT of 128 bytes but this can waste quite
135 /// a lot of space.
136 template <typename T, size_t ALIGNMENT = 64>
138 {
139 private:
140 
142 
143  using ut_DynamicValueMap = UT_ConcurrentHashMap<int, UT_UniquePtr<T>>;
144 
145 public:
146 
147  typedef T value_type;
148 
150  {
151  // Hello.
152 
153  // You're probably here because your build failed with a weird error.
154  // Please try to keep your TLS data as small as possible.
155  // UT_ThreadSpecificValue initializes a large block of pre-allocated
156  // values and this adds up over the large number of instances of it
157  // there are in the system.
158  // If large data structures are required, initialize them on-demand.
159  // See prm_ThreadData in $PRM/PRM_ChoiceList.C for an example.
160  SYS_STATIC_ASSERT(sizeof(T) <= 1024); // NOLINT
161 
162  // Ensure cache-line alignment.
163  SYS_STATIC_ASSERT(sizeof(myStaticThreadValues[0]) >= ALIGNMENT);
164  }
165 
166  /// Mark copy constructor as explicit to disallow accidental passing
167  /// by value to functions.
168  /// Assignment is NOT thread-safe when src is being concurrently modified!
171  {
172  *this = src;
173  }
174 
175  /// Assignment is NOT thread-safe when src is being concurrently modified!
177  {
178  if (this != &src)
179  {
180  for (int i = 0; i < theNbStaticThreadValues; ++i)
181  {
182  myStaticThreadValues[i] = src.myStaticThreadValues[i];
183  }
184  for (auto &&item : src.myDynamicThreadValues)
185  {
186  myDynamicThreadValues.emplace(
187  item.first, UTmakeUnique<T>(*item.second));
188  }
189  }
190  return *this;
191  }
192 
193  // Default move ctor/assign operators are fine.
194  /// @note Moving UT_ThreadSpecificValue is NOT thread-safe
195  /// @{
196  UT_ThreadSpecificValue(ThisType &&) = default;
197  UT_ThreadSpecificValue &operator=(ThisType &&) = default;
198  /// @}
199 
200  // SYSgetSTID() will "allocate" an index for this thread
201  // if it has never been called from this thread. The thread indices it
202  // returns start at 0. See $SYS/SYS_SequentialThreadIndex.h for more
203  // information.
204  //
205  /// Access the value for a particular thread index.
206  /// Note that the threads you care about may not have
207  /// been assigned sequentially! Always loop up to the maxthreads
208  /// value and be able to handle zero-initialized empty data.
209  T &getValueForThread(int thread_index)
210  {
211  // We want to catch cases where people are passing in the default 0
212  // thread value. However, for HDK users, we will fall back to computing
213  // it automatically for correctness.
214 #ifdef HAS_NO_TLS
215  return myStaticThreadValues[0];
216 #else
217  UT_ASSERT_P(thread_index > 0);
218  if (thread_index == 0)
219  thread_index = SYSgetSTID();
220 
222  if (thread_index < theNbStaticThreadValues)
223  return myStaticThreadValues[thread_index];
224 
225  return getDynamicValue(thread_index);
226 #endif
227  }
228  const T &getValueForThread(int thread_index) const
229  {
230 #ifdef HAS_NO_TLS
231  return myStaticThreadValues[0];
232 #else
233  UT_ASSERT_P(thread_index > 0);
234  if (thread_index == 0)
235  thread_index = SYSgetSTID();
236 
238  if (thread_index < theNbStaticThreadValues)
239  return myStaticThreadValues[thread_index];
240 
241  return getDynamicValue(thread_index);
242 #endif
243  }
244 
245  T &get() { return getValueForThread(SYSgetSTID()); }
246  T &local() { return getValueForThread(SYSgetSTID()); }
247 
248  const T &get() const { return getValueForThread(SYSgetSTID()); }
249  const T &local() const { return getValueForThread(SYSgetSTID()); }
250 
251  /// const_iterator
252  ///
253  /// @note The iterator iterates over ALL possible thread values, thus
254  /// you must be aware that the get() method will return the default value
255  /// for cases where this variable was never used in a thread.
257  {
258  public:
260  : myConstVal(0)
261  , myI(0)
262  {
263  }
265  : myConstVal(copy.myConstVal)
266  , myI(copy.myI)
267  {
268  }
270  {
271  if (this != &copy)
272  {
273  myConstVal = copy.myConstVal;
274  myI = copy.myI;
275  }
276  return *this;
277  }
278 
279  const T &get() const
280  {
281  if (myI < theNbStaticThreadValues)
282  return myConstVal->myStaticThreadValues[myI];
283  return *myDynamicIt->second;
284  }
285 
286  const T &operator*() const
287  {
288  return get();
289  }
290 
291  int thread() const
292  {
293  int val;
294  if (myI < theNbStaticThreadValues)
295  val = myI;
296  else
297  val = myDynamicIt->first;
299  }
300 
302  {
303  ++myI;
304  if (myI == theNbStaticThreadValues)
305  myDynamicIt = myConstVal->myDynamicThreadValues.begin();
306  else if (myI > theNbStaticThreadValues)
307  ++myDynamicIt;
308  return *this;
309  }
310 
312  {
313  return (myConstVal == right.myConstVal
314  && myI == right.myI);
315  }
317  {
318  return !(*this == right);
319  }
320 
321  protected:
323  : myConstVal(value)
324  , myI(start)
325  {
326  }
327 
329  int myI;
330  typename ut_DynamicValueMap::const_iterator myDynamicIt;
331 
332  template <typename TY, size_t A> friend class UT_ThreadSpecificValue;
333  };
334 
335  /// iterator
336  ///
337  /// @note The iterator iterates over ALL possible thread values, thus
338  /// you must be aware that the get() method will return the default value
339  /// for cases where this variable was never used in a thread.
340  class iterator : public const_iterator
341  {
342  public:
344  : const_iterator()
345  {
346  }
348  : const_iterator(copy)
349  {
350  }
352  {
353  this->const_iterator::operator=(copy);
354  return *this;
355  }
356 
357  T &get()
358  {
359  return const_cast<T &>(const_iterator::get());
360  }
361 
363  {
364  return get();
365  }
366 
368  {
369  const_iterator::operator++();
370  return *this;
371  }
372 
373  private:
375  : const_iterator((const ThisType *)value, start)
376  {
377  }
378 
379  template <typename TY, size_t A> friend class UT_ThreadSpecificValue;
380  };
381 
382  /// begin() const iterator
384  { return const_iterator(this, 0); }
385  /// end() const iterator
387  { return const_iterator(this, maxThreadsSeen()); }
388 
389  /// begin() iterator
390  iterator begin()
391  { return iterator(this, 0); }
392  /// end() iterator
393  iterator end()
394  { return iterator(this, maxThreadsSeen()); }
395 
396  /// The number of values that require iteration. Don't use this for
397  /// iteration - use iterators instead.
398  int maxThreadsSeen() const
399  {
400  // This may be less than the actual number if someone
401  // is currently writing to myDynamicValues, but then one
402  // is doing something very questionable.
403  return theNbStaticThreadValues + myDynamicThreadValues.size();
404  }
405 
406  /// Clear values for all threads, resetting to the initial state
407  void clear()
408  {
409  *this = ThisType();
410  }
411 
412  exint getMemoryUsage(bool inclusive) const
413  {
414  exint mem = inclusive ? sizeof(*this) : 0;
415 
416  mem += UTgetMemoryUsage(myDynamicThreadValues, false);
417 
418  // NOTE: UT_ThreadSpecificValue owns the content, not just the map.
419  mem += myDynamicThreadValues.size() * sizeof(T);
420 
421  return mem;
422  }
423 
424 private:
425  T &getDynamicValue(int thread_index) const
426  {
427  // Check if the value is in the map. If not, we'll hold the
428  // read-write accessor to that entry so that a new one can be
429  // constructed.
430  typename ut_DynamicValueMap::accessor a;
431  if (myDynamicThreadValues.insert(a, thread_index))
432  {
433  a->second = UTmakeUnique<T>();
434  if (SYSisPOD<T>())
435  ::memset((void *)a->second.get(), 0, sizeof(T)); // NOLINT
436  }
437  return *a->second;
438  }
439 
440 private:
442 
443  AlignType myStaticThreadValues[theNbStaticThreadValues];
444  mutable ut_DynamicValueMap myDynamicThreadValues;
445 
446  friend class const_iterator;
447 };
448 
449 #endif
const_iterator begin() const
begin() const iterator
typename std::enable_if< B, T >::type enable_if_t
Define Imath::enable_if_t to be std for C++14, equivalent for C++11.
#define SYS_STATIC_ASSERT(expr)
iterator end()
end() iterator
OIIO_UTIL_API bool copy(string_view from, string_view to, std::string &err)
const GLdouble * v
Definition: glcorearb.h:837
GLuint start
Definition: glcorearb.h:475
const_iterator end() const
end() const iterator
GLdouble right
Definition: glad.h:2817
const_iterator & operator=(const const_iterator &copy)
int64 exint
Definition: SYS_Types.h:125
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
ut_DynamicValueMap::const_iterator myDynamicIt
const_iterator(const ThisType *value, int start)
bool operator!=(const const_iterator &right)
iterator & operator=(const iterator &copy)
const T & getValueForThread(int thread_index) const
UT_ThreadSpecificValue & operator=(const ThisType &src)
Assignment is NOT thread-safe when src is being concurrently modified!
auto get(const UT_JSONValue::map_traverser &m) -> decltype(m.key())
Definition: UT_JSONValue.h:972
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:155
UT_ThreadSpecificValue(const ThisType &src)
void clear()
Clear values for all threads, resetting to the initial state.
int64 UTgetMemoryUsage(const UT_ConcurrentHashMap< K, V, H, A > &map, const bool inclusive)
iterator begin()
begin() iterator
SYS_API int SYSgetSTID()
bool operator==(const const_iterator &right)
GLuint GLfloat * val
Definition: glcorearb.h:1608
Definition: core.h:1131
#define const
Definition: zconf.h:214
exint getMemoryUsage(bool inclusive) const
T & getValueForThread(int thread_index)
GLenum src
Definition: glcorearb.h:1793