HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_DoubleLock.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  * NAME: UT_DoubleLock.h ( UT Library, C++)
7  *
8  * COMMENTS:
9  */
10 
11 #ifndef __UT_DoubleLock__
12 #define __UT_DoubleLock__
13 
14 #include "UT_API.h"
15 
16 #include "UT_NonCopyable.h"
17 #include "UT_Lock.h"
18 #include <SYS/SYS_MemoryOrder.h>
19 
20 ///
21 /// A double-checked lock. Only locks the thread lock when the value is 0.
22 ///
23 /// To use this lock:
24 ///
25 /// static UT_Lock theLock;
26 /// static OBJECT *theObj = 0;
27 ///
28 /// OBJECT *
29 /// getSingleton()
30 /// {
31 /// UT_DoubleLock<OBJECT *> lock(theLock, theObj);
32 ///
33 /// if (!lock.getValue())
34 /// {
35 /// // NOTE: This doesn't set theObj. theObj is set when
36 /// // destructing lock.
37 /// lock.setValue(new OBJECT());
38 /// }
39 /// return lock.getValue();
40 /// }
41 ///
42 /// NOTE: DO NOT roll your own double checked lock! If you
43 /// don't know when to use which memory fences and why,
44 /// odds are high that your custom implementation will be
45 /// thread-unsafe.
46 ///
47 /// For simple singleton initialization, consider using UT_Singleton.
48 ///
49 template <typename T, typename LOCK_T = UT_Lock>
51 {
52 public:
53  UT_DoubleLock(LOCK_T &lock, volatile T &val)
54  : myLock(lock)
55  , myValue(val)
56  , myPendingValue(0)
57  , myIsLocked(false)
58  , myNeedInit(false)
59  {
60  if (!val)
61  {
62  myLock.lock();
63  myIsLocked = true;
64  if (!val)
65  myNeedInit = true;
66  }
67  }
68 
70  {
71  if (myIsLocked)
72  {
73  if (myNeedInit)
74  {
75  UT_ASSERT_P(myPendingValue);
76 
77  // Ensure that the data that myPendingValue refers to is
78  // written out to main memory before setting myValue.
79  SYSstoreFence();
80 
81  myValue = myPendingValue;
82 
83  // NOTE: LOCK_T::unlock also does a store fence before
84  // unlocking, which ensures that myValue is written out to
85  // main memory before the lock is unlocked. If it didn't,
86  // the next thread might read myValue of 0 and try to
87  // initialize it again. The store fence above is also
88  // needed.
89  }
90  myLock.unlock();
91  }
92  }
93 
95  {
96  T v = myPendingValue;
97  if (v)
98  return v;
99 
100  v = myValue;
101 
102  // This load fence is to ensure that any side effects of allocation
103  // in another thread, including values in the heap itself, are not
104  // speculatively read before the read of myValue. Also, some places
105  // do more than allocate a single item pointed to by v, especially
106  // for cases with UT_DoubleLock<bool>, so those count as side
107  // effects here.
108  SYSloadFence();
109 
110  return v;
111  }
112  void setValue(T val)
113  {
114  UT_ASSERT_P(myNeedInit);
115  myPendingValue = val;
116  }
117 
118  /// Abort writing back out to the val passed in the constructor upon
119  /// destruction.
120  void abort()
121  {
122  myNeedInit = false;
123  }
124 
125 private:
126  LOCK_T &myLock;
127  volatile T &myValue;
128  T myPendingValue;
129  bool myIsLocked;
130  bool myNeedInit;
131 };
132 
133 #endif
134 
const GLdouble * v
Definition: glcorearb.h:836
#define SYSloadFence()
UT_DoubleLock(LOCK_T &lock, volatile T &val)
Definition: UT_DoubleLock.h:53
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:125
void setValue(T val)
#define SYSstoreFence()
GLuint GLfloat * val
Definition: glcorearb.h:1607