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  int thread() const
254  {
255  int val;
256  if (myI < theNbStaticThreadValues)
257  val = myI;
258  else
259  val = myDynamicIt->first;
261  }
262 
264  {
265  ++myI;
266  if (myI == theNbStaticThreadValues)
267  myDynamicIt = myConstVal->myDynamicThreadValues.begin();
268  else if (myI > theNbStaticThreadValues)
269  ++myDynamicIt;
270  return *this;
271  }
272 
274  {
275  return (myConstVal == right.myConstVal
276  && myI == right.myI);
277  }
279  {
280  return !(*this == right);
281  }
282 
283  protected:
285  : myConstVal(value)
286  , myI(start)
287  {
288  }
289 
291  int myI;
292  typename ut_DynamicValueMap::const_iterator myDynamicIt;
293 
294  template <typename TY, size_t A> friend class UT_ThreadSpecificValue;
295  };
296 
297  /// iterator
298  ///
299  /// @note The iterator iterates over ALL possible thread values, thus
300  /// you must be aware that the get() method will return the default value
301  /// for cases where this variable was never used in a thread.
302  class iterator : public const_iterator
303  {
304  public:
306  : const_iterator()
307  {
308  }
310  : const_iterator(copy)
311  {
312  }
314  {
315  this->const_iterator::operator=(copy);
316  return *this;
317  }
318 
319  T &get()
320  {
321  return const_cast<T &>(const_iterator::get());
322  }
323 
325  {
326  const_iterator::operator++();
327  return *this;
328  }
329 
330  private:
332  : const_iterator((const ThisType *)value, start)
333  {
334  }
335 
336  template <typename TY, size_t A> friend class UT_ThreadSpecificValue;
337  };
338 
339  /// begin() const iterator
341  { return const_iterator(this, 0); }
342  /// end() const iterator
344  { return const_iterator(this, maxThreadsSeen()); }
345 
346  /// begin() iterator
347  iterator begin()
348  { return iterator(this, 0); }
349  /// end() iterator
350  iterator end()
351  { return iterator(this, maxThreadsSeen()); }
352 
353  /// The number of values that require iteration. Don't use this for
354  /// iteration - use iterators instead.
355  int maxThreadsSeen() const
356  {
357  // This may be less than the actual number if someone
358  // is currently writing to myDynamicValues, but then one
359  // is doing something very questionable.
360  return theNbStaticThreadValues + myDynamicThreadValues.size();
361  }
362 
363  /// Clear values for all threads, resetting to the initial state
364  void clear()
365  {
366  *this = ThisType();
367  }
368 
369  exint getMemoryUsage(bool inclusive) const
370  {
371  exint mem = inclusive ? sizeof(*this) : 0;
372 
373  mem += UTgetMemoryUsage(myDynamicThreadValues, false);
374 
375  // NOTE: UT_ThreadSpecificValue owns the content, not just the map.
376  mem += myDynamicThreadValues.size() * sizeof(T);
377 
378  return mem;
379  }
380 
381 private:
382  T &getDynamicValue(int thread_index) const
383  {
384  // Check if the value is in the map. If not, we'll hold the
385  // read-write accessor to that entry so that a new one can be
386  // constructed.
387  typename ut_DynamicValueMap::accessor a;
388  if (myDynamicThreadValues.insert(a, thread_index))
389  {
390  a->second = new T;
391  if (SYSisPOD<T>())
392  ::memset((void *)a->second, 0, sizeof(T)); // NOLINT
393  }
394  return *a->second;
395  }
396 
397 private:
399 
400  AlignType myStaticThreadValues[theNbStaticThreadValues];
401  mutable ut_DynamicValueMap myDynamicThreadValues;
402 
403  friend class const_iterator;
404 };
405 
406 #endif
const_iterator begin() const
begin() const iterator
#define SYS_STATIC_ASSERT(expr)
iterator end()
end() iterator
GLuint const GLfloat * val
Definition: glew.h:2794
const_iterator end() const
end() const iterator
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:9477
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)
const GLdouble * v
Definition: glew.h:1391
bool operator!=(const const_iterator &right)
iterator & operator=(const iterator &copy)
const T & getValueForThread(int thread_index) const
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:134
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)
GLfloat right
Definition: glew.h:15186
GLuint start
Definition: glew.h:1253
iterator begin()
begin() iterator
SYS_API int SYSgetSTID()
bool operator==(const const_iterator &right)
OIIO_API bool copy(string_view from, string_view to, std::string &err)
#define const
Definition: zconf.h:214
exint getMemoryUsage(bool inclusive) const
GLsizei const GLfloat * value
Definition: glew.h:1849
T & getValueForThread(int thread_index)