HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_Singleton.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_Singleton.h (UT Library, C++)
7  *
8  * COMMENTS: Classes for on-demand construction of singletons or
9  * pointer member variables.
10  */
11 
12 #ifndef __UT_Singleton__
13 #define __UT_Singleton__
14 
15 #include "UT_NonCopyable.h"
16 #include "UT_LockUtil.h"
17 #include <SYS/SYS_MemoryOrder.h>
18 
19 class UT_Lock;
20 
21 namespace {
22 
23 /// This is a helper function for UT_Singleton and UT_SingletonWithLock.
24 /// Do NOT use it directly unless you know what you're doing.
25 template <typename T, class LOCK>
26 T &
27 UTacquireSingleton(T *volatile &p, LOCK &lock)
28 {
29  // Read into temp to avoid re-reading p with "return *p;",
30  // since p is only written once.
31  T *temp = p;
32  if (!temp)
33  {
34  typename LOCK::Scope autolock(lock);
35 
36  // Read into temp to avoid re-reading p with "return *p;",
37  // since p is only written once.
38  temp = p;
39  if (!temp)
40  {
41  temp = new T();
42 
43  // Ensure that the data that temp refers to is written
44  // out to main memory before setting p.
45  // Without this store fence, the CPU may re-order the
46  // storing of the data in the cache, and another
47  // thread could receive p as non-NULL before
48  // the data pointed-to by p is initialized
49  // in main memory, so the thread could see garbage data.
50  SYSstoreFence();
51 
52  p = temp;
53  }
54  }
55 
56  // NOTE: This load fence is only necessary for preventing speculative
57  // reads of any side effects of new T() producing values from
58  // before the allocation.
59  //
60  // Even if there aren't explicit side effects, there's
61  // the side effect of a new memory block being allocated
62  // on the heap, so just in case, it's probably better to
63  // leave this here. For (a poor) example, if the heap tracks
64  // how much memory it uses, and if the CPU does a following read
65  // of that count speculatively *before* the read of p, the value
66  // read could be the value from *before* p was allocated.
67  SYSloadFence();
68 
69  return *temp;
70 }
71 
72 /// This is a helper function for UT_Singleton and UT_SingletonWithLock.
73 /// This is the same as UTacquireSingleton(T *volatile &p, LOCK &lock),
74 /// except that it passes s, of type S, to the T constructor.
75 /// Do NOT use it directly unless you know what you're doing.
76 template <typename T, class LOCK, typename S>
77 T &
78 UTacquireSingleton(T *volatile &p, LOCK &lock, S s)
79 {
80  // Read into temp to avoid re-reading p with "return *p;",
81  // since p is only written once.
82  T *temp = p;
83  if (!temp)
84  {
85  typename LOCK::Scope autolock(lock);
86 
87  // Read into temp to avoid re-reading p with "return *p;",
88  // since p is only written once.
89  temp = p;
90  if (!temp)
91  {
92  temp = new T(s);
93 
94  // Ensure that the data that temp refers to is written
95  // out to main memory before setting p.
96  // Without this store fence, the CPU may re-order the
97  // storing of the data in the cache, and another
98  // thread could receive p as non-NULL before
99  // the data pointed-to by p is initialized
100  // in main memory, so the thread could see garbage data.
101  SYSstoreFence();
102 
103  p = temp;
104  }
105  }
106 
107  // NOTE: This load fence is only necessary for preventing speculative
108  // reads of any side effects of allocfunc(param) producing values
109  // from before the allocation.
110  //
111  // Even if there aren't explicit side effects, there's
112  // the side effect of a new memory block being allocated
113  // on the heap, so just in case, it's probably better to
114  // leave this here. For (a poor) example, if the heap tracks
115  // how much memory it uses, and if the CPU does a following read
116  // of that count speculatively *before* the read of p, the value
117  // read could be the value from *before* p was allocated.
118  SYSloadFence();
119 
120  return *temp;
121 }
122 
123 } // end of anonymous namespace
124 
125 /// This is the same as UT_SingletonWithLock, except referencing an
126 /// existing lock in get(), instead of holding its own. This can be useful
127 /// for avoiding having many locks as member variables when using
128 /// UT_Singleton for pointer member variables that are allocated
129 /// on-demand, or if another lock would need to be acquired in order
130 /// to run new T().
131 template <typename T, bool DESTRUCTONEXIT=false>
133 {
134 public:
135  UT_Singleton() : myPointer(0) {}
137  {
138  if (DESTRUCTONEXIT)
139  delete myPointer;
140  }
141 
143 
144  template <class LOCK>
145  T &get(LOCK &lock)
146  {
147  return UTacquireSingleton(myPointer, lock);
148  }
149 
150  template <class LOCK, typename S>
151  T &get(LOCK &lock, S s)
152  {
153  return UTacquireSingleton(myPointer, lock, s);
154  }
155 
156  /// NOTE: Even though unsafeSet locks, it is still unsafe, because
157  /// other threads may have called get(), gotten a valid pointer,
158  /// and be using it when unsafeSet destructs it.
159  template <class LOCK>
160  void unsafeSet(LOCK &lock, T *newp)
161  {
162  T *oldp = myPointer;
163  if (oldp != newp)
164  {
165  typename LOCK::Scope autolock(lock);
166 
167  oldp = myPointer;
168  if (oldp != newp)
169  {
170  // Make sure to save out the data pointed to by newp to main
171  // memory before setting myPointer to it.
172  SYSstoreFence();
173 
174  myPointer = newp;
175 
176  if (oldp)
177  delete oldp;
178  }
179  }
180  }
181 
182 private:
183  T *volatile myPointer;
184 };
185 
186 ///
187 /// This is a singleton constructed on-demand with a double-checked lock.
188 /// The lock is only locked if get() is called when myPointer is 0.
189 ///
190 /// This is normally simpler to use than a UT_DoubleLock, and this should
191 /// be used for all on-demand singleton construction, as well as for
192 /// pointer member variables that are allocated on-demand, if applicable.
193 /// If it is preferable in such a case to use an existing lock instead of
194 /// using the lock member in this class, please use UT_Singleton.
195 ///
196 /// To use this class in the simplest case, where OBJECT is to be
197 /// default-constructed:
198 ///
199 /// static UT_SingletonWithLock<OBJECT> theSingleton;
200 ///
201 /// void useSingleton()
202 /// {
203 /// OBJECT &obj = theSingleton.get();
204 /// ...
205 /// }
206 ///
207 /// When special construction or destruction are necessary, subclass
208 /// OBJECT:
209 ///
210 /// class OBJECTWrapper : public OBJECT
211 /// {
212 /// public:
213 /// OBJECTWrapper()
214 /// : OBJECT(12345)
215 /// { doSpecialInit(67890); }
216 /// ~OBJECTWrapper()
217 /// { doBeforeDestruction(7654); }
218 /// };
219 ///
220 /// static UT_SingletonWithLock<OBJECTWrapper> theSingleton;
221 ///
222 /// void useSingleton()
223 /// {
224 /// OBJECT &obj = theSingleton.get();
225 /// ...
226 /// }
227 ///
228 /// NOTE: Do not roll your own on-demand singleton construction!
229 /// This class should be threadsafe, and it's very easy to
230 /// miss a store memory fence without noticing for a very
231 /// long time.
232 ///
233 template <typename T, bool DESTRUCTONEXIT=false, class LOCK=UT_Lock>
234 class UT_SingletonWithLock : public UT_Singleton<T, DESTRUCTONEXIT>
235 {
236 public:
238 
239  T &get()
240  {
241  return Base::get(myLock);
242  }
243 
244  template <typename S>
245  T &get(S s)
246  {
247  return Base::get(myLock, s);
248  }
249 
250  /// NOTE: Even though unsafeSet locks, it is still unsafe, because
251  /// other threads may have called get(), gotten a valid pointer,
252  /// and be using it when unsafeSet destructs it.
253  void unsafeSet(T *newp)
254  {
255  Base::unsafeSet(myLock, newp);
256  }
257 
258  T &operator*() { return get(); }
259  T *operator->() { return &get(); }
260 private:
261  LOCK myLock;
262 };
263 
264 
265 
266 
267 #endif
268 
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the and then *wait for them to all complete We provide a helper class
Definition: thread.h:623
#define SYSloadFence()
GLdouble s
Definition: glad.h:3009
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
void unsafeSet(LOCK &lock, T *newp)
Definition: UT_Singleton.h:160
#define SYSstoreFence()
void unsafeSet(T *newp)
Definition: UT_Singleton.h:253
T & get(LOCK &lock)
Definition: UT_Singleton.h:145
UT_Singleton< T, DESTRUCTONEXIT > Base
Definition: UT_Singleton.h:237