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 <SYS/SYS_Align.h>
45 #include <SYS/SYS_StaticAssert.h>
46 #include <SYS/SYS_Types.h>
47 #include <SYS/SYS_TypeTraits.h>
48 
49 #include <type_traits>
50 #include <string.h>
51 
52 
53 #ifdef HAS_NO_TLS
54 static const size_t theNbStaticThreadValues = 1;
55 #else
56 static const size_t theNbStaticThreadValues = 32;
57 #endif
58 
59 namespace UT_ThreadSpecificValueDetail
60 {
61  template<typename U, size_t Align, typename Enable = void>
62  class AlignType;
63 
64  template<typename U, size_t Align>
65  class alignas(Align) AlignType<U, Align, std::enable_if_t<(Align > 0)>>
66  {
67  public:
69  {
70  // If T is an object, the default constructor will have been
71  // called. Otherwise, initialize the memory to 0.
72  if (SYSisPOD<U>())
73  memset((void *)&myValue, 0, sizeof(U)); // NOLINT
74  }
75 
76  operator const U &() const
77  {
78  return myValue;
79  }
80  operator U &()
81  {
82  return myValue;
83  }
84 
85  U &operator=(const U &v)
86  {
87  if (&v != &myValue)
88  myValue = v;
89  return myValue;
90  }
91 
92  private:
93  U myValue;
94  };
95 
96  template<typename U, size_t Align>
97  class AlignType<U, Align, std::enable_if_t<(Align == 0)>>
98  {
99  public:
101  {
102  // If T is an object, the default constructor will have been
103  // called. Otherwise, initialize the memory to 0.
104  if (SYSisPOD<U>())
105  memset((void *)&myValue, 0, sizeof(U));
106  }
107 
108  operator const U &() const
109  {
110  return myValue;
111  }
112  operator U &()
113  {
114  return myValue;
115  }
116 
117  U &operator=(const U &v)
118  {
119  if (&v != &myValue)
120  myValue = v;
121  return myValue;
122  }
123 
124  private:
125  U myValue;
126  };
127 }
128 
129 /// A class for storing thread local values of type T. By default, the
130 /// alignment of the type T's will be 64-byte aligned but this can be adjusted
131 /// by supplying the ALIGNMENT template parameter, or use 0 to disable. For
132 /// optimal performance, use an ALIGNMENT of 128 bytes but this can waste quite
133 /// a lot of space.
134 template <typename T, size_t ALIGNMENT = 64>
136 {
137 private:
138 
140 
141  using ut_DynamicValueMap = UT_ConcurrentHashMap<int, T *>;
142 
143 public:
144 
145  typedef T value_type;
146 
148  {
149  // Hello.
150 
151  // You're probably here because your build failed with a weird error.
152  // Please try to keep your TLS data as small as possible.
153  // UT_ThreadSpecificValue initializes a large block of pre-allocated
154  // values and this adds up over the large number of instances of it
155  // there are in the system.
156  // If large data structures are required, initialize them on-demand.
157  // See prm_ThreadData in $PRM/PRM_ChoiceList.C for an example.
158  SYS_STATIC_ASSERT(sizeof(T) <= 1024); // NOLINT
159 
160  // Ensure cache-line alignment.
161  SYS_STATIC_ASSERT(sizeof(myStaticThreadValues[0]) >= ALIGNMENT);
162  }
163 
165  {
166  typename ut_DynamicValueMap::iterator it;
167  for (it = myDynamicThreadValues.begin();
168  it != myDynamicThreadValues.end(); ++it)
169  delete it->second;
170  }
171 
172  // Only allow assignment, disallow copy construction
173  UT_ThreadSpecificValue(const ThisType &) = delete;
174 
175  // SYSgetSTID() will "allocate" an index for this thread
176  // if it has never been called from this thread. The thread indices it
177  // returns start at 0. See $SYS/SYS_SequentialThreadIndex.h for more
178  // information.
179  //
180  /// Access the value for a particular thread index.
181  /// Note that the threads you care about may not have
182  /// been assigned sequentially! Always loop up to the maxthreads
183  /// value and be able to handle zero-initialized empty data.
184  T &getValueForThread(int thread_index)
185  {
186  // We want to catch cases where people are passing in the default 0
187  // thread value. However, for HDK users, we will fall back to computing
188  // it automatically for correctness.
189 #ifdef HAS_NO_TLS
190  return myStaticThreadValues[0];
191 #else
192  UT_ASSERT_P(thread_index > 0);
193  if (thread_index == 0)
194  thread_index = SYSgetSTID();
195 
197  if (thread_index < theNbStaticThreadValues)
198  return myStaticThreadValues[thread_index];
199 
200  return getDynamicValue(thread_index);
201 #endif
202  }
203  const T &getValueForThread(int thread_index) const
204  {
205 #ifdef HAS_NO_TLS
206  return myStaticThreadValues[0];
207 #else
208  UT_ASSERT_P(thread_index > 0);
209  if (thread_index == 0)
210  thread_index = SYSgetSTID();
211 
213  if (thread_index < theNbStaticThreadValues)
214  return myStaticThreadValues[thread_index];
215 
216  return getDynamicValue(thread_index);
217 #endif
218  }
219 
220  T &get() { return getValueForThread(SYSgetSTID()); }
221  T &local() { return getValueForThread(SYSgetSTID()); }
222 
223  const T &get() const { return getValueForThread(SYSgetSTID()); }
224  const T &local() const { return getValueForThread(SYSgetSTID()); }
225 
226  /// const_iterator
227  ///
228  /// @note The iterator iterates over ALL possible thread values, thus
229  /// you must be aware that the get() method will return the default value
230  /// for cases where this variable was never used in a thread.
232  {
233  public:
235  : myConstVal(0)
236  , myI(0)
237  {
238  }
240  : myConstVal(copy.myConstVal)
241  , myI(copy.myI)
242  {
243  }
245  {
246  if (this != &copy)
247  {
248  myConstVal = copy.myConstVal;
249  myI = copy.myI;
250  }
251  return *this;
252  }
253 
254  const T &get() const
255  {
256  if (myI < theNbStaticThreadValues)
257  return myConstVal->myStaticThreadValues[myI];
258  return *myDynamicIt->second;
259  }
260 
261  const T &operator*() const
262  {
263  return get();
264  }
265 
266  int thread() const
267  {
268  int val;
269  if (myI < theNbStaticThreadValues)
270  val = myI;
271  else
272  val = myDynamicIt->first;
274  }
275 
277  {
278  ++myI;
279  if (myI == theNbStaticThreadValues)
280  myDynamicIt = myConstVal->myDynamicThreadValues.begin();
281  else if (myI > theNbStaticThreadValues)
282  ++myDynamicIt;
283  return *this;
284  }
285 
287  {
288  return (myConstVal == right.myConstVal
289  && myI == right.myI);
290  }
292  {
293  return !(*this == right);
294  }
295 
296  protected:
298  : myConstVal(value)
299  , myI(start)
300  {
301  }
302 
304  int myI;
305  typename ut_DynamicValueMap::const_iterator myDynamicIt;
306 
307  template <typename TY, size_t A> friend class UT_ThreadSpecificValue;
308  };
309 
310  /// iterator
311  ///
312  /// @note The iterator iterates over ALL possible thread values, thus
313  /// you must be aware that the get() method will return the default value
314  /// for cases where this variable was never used in a thread.
315  class iterator : public const_iterator
316  {
317  public:
319  : const_iterator()
320  {
321  }
323  : const_iterator(copy)
324  {
325  }
327  {
328  this->const_iterator::operator=(copy);
329  return *this;
330  }
331 
332  T &get()
333  {
334  return const_cast<T &>(const_iterator::get());
335  }
336 
338  {
339  return get();
340  }
341 
343  {
344  const_iterator::operator++();
345  return *this;
346  }
347 
348  private:
350  : const_iterator((const ThisType *)value, start)
351  {
352  }
353 
354  template <typename TY, size_t A> friend class UT_ThreadSpecificValue;
355  };
356 
357  /// begin() const iterator
359  { return const_iterator(this, 0); }
360  /// end() const iterator
362  { return const_iterator(this, maxThreadsSeen()); }
363 
364  /// begin() iterator
365  iterator begin()
366  { return iterator(this, 0); }
367  /// end() iterator
368  iterator end()
369  { return iterator(this, maxThreadsSeen()); }
370 
371  /// The number of values that require iteration. Don't use this for
372  /// iteration - use iterators instead.
373  int maxThreadsSeen() const
374  {
375  // This may be less than the actual number if someone
376  // is currently writing to myDynamicValues, but then one
377  // is doing something very questionable.
378  return theNbStaticThreadValues + myDynamicThreadValues.size();
379  }
380 
381  /// Clear values for all threads, resetting to the initial state
382  void clear()
383  {
384  *this = ThisType();
385  }
386 
387  exint getMemoryUsage(bool inclusive) const
388  {
389  exint mem = inclusive ? sizeof(*this) : 0;
390 
391  mem += UTgetMemoryUsage(myDynamicThreadValues, false);
392 
393  // NOTE: UT_ThreadSpecificValue owns the content, not just the map.
394  mem += myDynamicThreadValues.size() * sizeof(T);
395 
396  return mem;
397  }
398 
399 private:
400  T &getDynamicValue(int thread_index) const
401  {
402  // Check if the value is in the map. If not, we'll hold the
403  // read-write accessor to that entry so that a new one can be
404  // constructed.
405  typename ut_DynamicValueMap::accessor a;
406  if (myDynamicThreadValues.insert(a, thread_index))
407  {
408  a->second = new T;
409  if (SYSisPOD<T>())
410  ::memset((void *)a->second, 0, sizeof(T)); // NOLINT
411  }
412  return *a->second;
413  }
414 
415 private:
417 
418  AlignType myStaticThreadValues[theNbStaticThreadValues];
419  mutable ut_DynamicValueMap myDynamicThreadValues;
420 
421  friend class const_iterator;
422 };
423 
424 #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)
GLuint start
Definition: glcorearb.h:475
const_iterator end() const
end() const iterator
const_iterator & operator=(const const_iterator &copy)
int64 exint
Definition: SYS_Types.h:125
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
GLfloat right
Definition: glew.h:15525
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:152
const GLdouble * v
Definition: glcorearb.h:837
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
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
GLuint GLfloat * val
Definition: glcorearb.h:1608
SYS_API int SYSgetSTID()
bool operator==(const const_iterator &right)
Definition: core.h:1131
#define const
Definition: zconf.h:214
exint getMemoryUsage(bool inclusive) const
T & getValueForThread(int thread_index)