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_SpinLock.h"
34 #include "UT_ConcurrentHashMap.h"
35 #include <SYS/SYS_Align.h>
36 #include <SYS/SYS_TypeTraits.h>
37 #include <SYS/SYS_Platform.h>
39 #include <limits>
40 #include <SYS/SYS_Math.h>
41 #include <SYS/SYS_StaticAssert.h>
42 
43 #ifdef HAS_NO_TLS
44 static const size_t theNbStaticThreadValues = 1;
45 #else
46 static const size_t theNbStaticThreadValues = 32;
47 #endif
48 
49 template <typename T>
51 {
52 private:
53  template <typename U>
54  class SYS_ALIGN(64) AlignType
55  {
56  public:
57  AlignType()
58  {
59  // If T is an object, the default constructor will have been
60  // called. Otherwise, initialize the memory to 0.
61  if (SYSisPOD<U>())
62  memset((void *)&myValue, 0, sizeof(U));
63  }
64 
65  operator const U &() const
66  {
67  return myValue;
68  }
69  operator U &()
70  {
71  return myValue;
72  }
73 
74  U &operator=(const U &v)
75  {
76  if (&v != &myValue)
77  myValue = v;
78  return myValue;
79  }
80 
81  private:
82  U myValue;
83  };
84 
85  typedef UT_ConcurrentHashMap<int, T *> ut_DynamicValueMap;
86 
87 public:
88 
89  typedef T value_type;
90 
92  {
93  // Hello.
94 
95  // You're probably here because your build failed with a weird error.
96  // Please try to keep your TLS data as small as possible.
97  // UT_ThreadSpecificValue initializes a large block of pre-allocated
98  // values and this adds up over the large number of instances of it
99  // there are in the system.
100  // If large data structures are required, initialize them on-demand.
101  // See prm_ThreadData in $PRM/PRM_ChoiceList.C for an example.
102  SYS_STATIC_ASSERT(sizeof(T) <= 1024);
103 
104  // Ensure cache-line alignment.
105  SYS_STATIC_ASSERT(sizeof(myStaticThreadValues[0]) >= 64);
106  }
107 
109  {
110  typename ut_DynamicValueMap::iterator it;
111  for (it = myDynamicThreadValues.begin();
112  it != myDynamicThreadValues.end(); ++it)
113  delete it->second;
114  }
115 
116  // SYSgetSTID() will "allocate" an index for this thread
117  // if it has never been called from this thread. The thread indices it
118  // returns start at 0. See $SYS/SYS_SequentialThreadIndex.h for more
119  // information.
120  //
121  /// Access the value for a particular thread index.
122  /// Note that the threads you care about may not have
123  /// been assigned sequentially! Always loop up to the maxthreads
124  /// value and be able to handle zero-initialized empty data.
125  T &getValueForThread(int thread_index)
126  {
127  // We want to catch cases where people are passing in the default 0
128  // thread value. However, for HDK users, we will fall back to computing
129  // it automatically for correctness.
130 #ifdef HAS_NO_TLS
131  return myStaticThreadValues[0];
132 #else
133  UT_ASSERT_P(thread_index > 0);
134  if (thread_index == 0)
135  thread_index = SYSgetSTID();
136 
138  if (thread_index < theNbStaticThreadValues)
139  return myStaticThreadValues[thread_index];
140 
141  return getDynamicValue(thread_index);
142 #endif
143  }
144  const T &getValueForThread(int thread_index) const
145  {
146 #ifdef HAS_NO_TLS
147  return myStaticThreadValues[0];
148 #else
149  UT_ASSERT_P(thread_index > 0);
150  if (thread_index == 0)
151  thread_index = SYSgetSTID();
152 
154  if (thread_index < theNbStaticThreadValues)
155  return myStaticThreadValues[thread_index];
156 
157  return getDynamicValue(thread_index);
158 #endif
159  }
160 
161  T &get() { return getValueForThread(SYSgetSTID()); }
163 
164  const T &get() const { return getValueForThread(SYSgetSTID()); }
165  const T &local() const { return getValueForThread(SYSgetSTID()); }
166 
167  /// const_iterator
168  ///
169  /// @note The iterator iterates over ALL possible thread values, thus
170  /// you must be aware that the get() method will return the default value
171  /// for cases where this variable was never used in a thread.
173  {
174  public:
176  : myConstVal(0)
177  , myI(0)
178  {
179  }
181  : myConstVal(copy.myConstVal)
182  , myI(copy.myI)
183  {
184  }
186  {
187  if (this != &copy)
188  {
189  myConstVal = copy.myConstVal;
190  myI = copy.myI;
191  }
192  return *this;
193  }
194 
195  const T &get() const
196  {
197  if (myI < theNbStaticThreadValues)
198  return myConstVal->myStaticThreadValues[myI];
199  return *myDynamicIt->second;
200  }
201 
202  int thread() const
203  {
204  int val;
205  if (myI < theNbStaticThreadValues)
206  val = myI;
207  else
208  val = myDynamicIt->first;
210  }
211 
213  {
214  ++myI;
215  if (myI == theNbStaticThreadValues)
216  myDynamicIt = myConstVal->myDynamicThreadValues.begin();
217  else if (myI > theNbStaticThreadValues)
218  ++myDynamicIt;
219  return *this;
220  }
221 
222  bool operator==(const const_iterator &right)
223  {
224  return (myConstVal == right.myConstVal
225  && myI == right.myI);
226  }
227  bool operator!=(const const_iterator &right)
228  {
229  return !(*this == right);
230  }
231 
232  protected:
234  : myConstVal(value)
235  , myI(start)
236  {
237  }
238 
240  int myI;
241  typename ut_DynamicValueMap::const_iterator myDynamicIt;
242 
243  template <typename TY> friend class UT_ThreadSpecificValue;
244  };
245 
246  /// iterator
247  ///
248  /// @note The iterator iterates over ALL possible thread values, thus
249  /// you must be aware that the get() method will return the default value
250  /// for cases where this variable was never used in a thread.
251  class iterator : public const_iterator
252  {
253  public:
255  : const_iterator()
256  {
257  }
258  iterator(const iterator &copy)
259  : const_iterator(copy)
260  {
261  }
263  {
264  this->const_iterator::operator=(copy);
265  return *this;
266  }
267 
268  T &get()
269  {
270  return const_cast<T &>(const_iterator::get());
271  }
272 
274  {
276  return *this;
277  }
278 
279  private:
281  : const_iterator((const UT_ThreadSpecificValue<T> *)value, start)
282  {
283  }
284 
285  template <typename TY> friend class UT_ThreadSpecificValue;
286  };
287 
288  /// begin() const iterator
290  { return const_iterator(this, 0); }
291  /// end() const iterator
293  { return const_iterator(this, maxThreadsSeen()); }
294 
295  /// begin() iterator
296  iterator begin()
297  { return iterator(this, 0); }
298  /// end() iterator
299  iterator end()
300  { return iterator(this, maxThreadsSeen()); }
301 
302  /// The number of values that require iteration. Don't use this for
303  /// iteration - use iterators instead.
304  int maxThreadsSeen() const
305  {
306  // This may be less than the actual number if someone
307  // is currently writing to myDynamicValues, but then one
308  // is doing something very questionable.
309  return theNbStaticThreadValues + myDynamicThreadValues.size();
310  }
311 
312  /// Clear values for all threads, resetting to the initial state
313  void clear()
314  {
315  *this = UT_ThreadSpecificValue<T>();
316  }
317 
318  exint getMemoryUsage(bool inclusive) const
319  {
320  exint mem = inclusive ? sizeof(*this) : 0;
321 
322  mem += UTgetMemoryUsage(myDynamicThreadValues, false);
323 
324  // NOTE: UT_ThreadSpecificValue owns the content, not just the map.
325  mem += myDynamicThreadValues.size() * sizeof(T);
326 
327  return mem;
328  }
329 
330 private:
331  UT_ThreadSpecificValue(const UT_ThreadSpecificValue<T> &); // unimplemented
332 
333  T &getDynamicValue(int thread_index) const
334  {
335  // Check if the value is in the map. If not, we'll hold the
336  // read-write accessor to that entry so that a new one can be
337  // constructed.
338  typename ut_DynamicValueMap::accessor a;
339  if (myDynamicThreadValues.insert(a, thread_index))
340  {
341  a->second = new T;
342  if (SYSisPOD<T>())
343  ::memset((void *)a->second, 0, sizeof(T));
344  }
345  return *a->second;
346  }
347 
348 private:
349  AlignType<T> myStaticThreadValues[theNbStaticThreadValues];
350  mutable ut_DynamicValueMap myDynamicThreadValues;
351 
352  friend class const_iterator;
353 };
354 
355 #endif
const_iterator(const UT_ThreadSpecificValue< T > *value, int start)
exint getMemoryUsage(bool inclusive) const
#define SYS_STATIC_ASSERT(expr)
bool operator!=(const const_iterator &right)
bool operator==(const const_iterator &right)
const GLdouble * v
Definition: glcorearb.h:836
GLuint start
Definition: glcorearb.h:474
const_iterator & operator=(const const_iterator &copy)
const T & getValueForThread(int thread_index) const
const UT_ThreadSpecificValue< T > * myConstVal
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1221
T & getValueForThread(int thread_index)
ut_DynamicValueMap::const_iterator myDynamicIt
iterator & operator=(const iterator &copy)
#define SYS_ALIGN(b)
Definition: SYS_Align.h:101
int64 exint
Definition: SYS_Types.h:116
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:125
const_iterator end() const
end() const iterator
iterator begin()
begin() iterator
int64 UTgetMemoryUsage(const UT_ConcurrentHashMap< K, V, H, A > &map, const bool inclusive)
iterator end()
end() iterator
GLsizei const GLfloat * value
Definition: glcorearb.h:823
SYS_API int SYSgetSTID()
GLuint GLfloat * val
Definition: glcorearb.h:1607
void clear()
Clear values for all threads, resetting to the initial state.
#define const
Definition: zconf.h:214
const_iterator begin() const
begin() const iterator