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