HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_LockUtil.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_LockUtil.h ( UT Library, C++)
7  *
8  * COMMENTS: Templates for special types of thread locks
9  */
10 
11 #ifndef UT_LOCKUTILS_H
12 #define UT_LOCKUTILS_H
13 
14 #include "UT_API.h"
15 #include "UT_Assert.h"
16 #include "UT_NonCopyable.h"
17 #include "UT_ValArray.h"
18 #include <stdio.h>
19 
20 
21 /// A simple class to acquire a lock and ensure that it is released when it
22 /// goes out of scope. It locks with a lightweight lock (which is the most
23 /// common type used). This works with both normal and threadlock types.
24 /// @note This is similar to hboost::lock_guard
25 template <class Lock>
27 {
28 public:
30  : myLock(lock)
31  { lock.lock(); }
32 
33  ~UT_AutoLockType() { myLock.unlock(); }
34 
35 private:
36  Lock &myLock;
37 };
38 
39 
40 class UT_EmptyScope {};
41 
42 
43 /// Like UT_AutoLockType except it supports explicit control of when to lock
44 /// (or unlock) while providing exception safety.
45 /// @note This is similar to hboost::unique_lock
46 /// @note The same UT_LockScopeType object cannot be safely accessed from
47 /// multiple threads!
48 template <class Lock>
50 {
51 public:
52  UT_LockScopeType() = delete;
53 
54  /// Construct without mutex. Use acquire() to give it a mutex.
56  : myMutex(NULL)
57  , myIsLocked(false)
58  {
59  }
60 
61  /// Construct with given mutex. Acquires it by default.
62  explicit UT_LockScopeType(Lock &mutex, bool acquire = true)
63  : myMutex(&mutex)
64  , myIsLocked(false)
65  {
66  if (acquire)
67  lock();
68  }
70  : myMutex(scope.myMutex)
71  , myIsLocked(scope.myIsLocked)
72  {
73  scope.myMutex = nullptr;
74  scope.myIsLocked = false;
75  }
77  {
78  if (myIsLocked)
79  unlock();
80  }
81 
82  /// Lock the mutex
83  void lock()
84  {
85  if (myIsLocked || !myMutex)
86  {
87  UT_ASSERT(!"Can't relock");
88  return;
89  }
90  myMutex->lock();
91  myIsLocked = true;
92  }
93 
94  /// Assign a new mutex reference and lock it
95  /// @note Unlocks the old mutex.
96  void lock(Lock &mutex)
97  {
98  if (myIsLocked)
99  unlock();
100  myMutex = &mutex;
101  myMutex->lock();
102  myIsLocked = true;
103  }
104 
105  /// Try to lock the mutex, return immediately false if someone else owns it
106  bool tryLock()
107  {
108  if (myIsLocked || !myMutex)
109  {
110  UT_ASSERT(!"Can't relock");
111  return false;
112  }
113  myIsLocked = myMutex->tryLock();
114  return myIsLocked;
115  }
116 
117  /// Try to lock the mutex, return immediately false if someone else owns it.
118  /// @note Unlocks the old mutex.
119  bool tryLock(Lock &mutex)
120  {
121  if (myIsLocked)
122  unlock();
123  myMutex = &mutex;
124  myIsLocked = myMutex->tryLock();
125  return myIsLocked;
126  }
127 
128  /// Lock the mutex, return false if a dead lock was detected.
129  /// @note Only works if the underlying mutex supports it.
130  bool safeLock(Lock &mutex)
131  {
132  if (myIsLocked)
133  unlock();
134  myMutex = &mutex;
135 
136  if (!mutex.safeLock())
137  return false;
138 
139  myIsLocked = true;
140  return true;
141  }
142 
143  /// Unlock the mutex before this object is deleted.
144  void unlock()
145  {
146  if (!myIsLocked || !myMutex)
147  {
148  UT_ASSERT(!"Can't unlock without locking first");
149  return;
150  }
151  myMutex->unlock();
152  myIsLocked = false;
153  }
154 
155 private:
156  Lock * myMutex;
157  bool myIsLocked;
158 };
159 
160 
161 /// An empty implementation of the lock interface (for easily removing all
162 /// locks from an application).
164 {
165 public:
166  explicit UT_NullLock(int /*lockstate*/ = 0) {}
167 
168  bool lock(int) { return true; }
169  bool safeLock() { return true; }
170  void lock() {}
171  void unlock() {}
172  bool isLocked() { return false; }
173 
175 };
176 
177 
178 template <class Lock>
180 {
181 public:
182  UT_DebugLockType(const char *name, int lockstate=0, bool threadlock=false)
183  : myLock(lockstate, threadlock),
184  myName(name)
185  { }
187  : myLock(rhs.myLock),
188  myName(rhs.myName)
189  { }
191  {
192  fprintf(stderr, "Lock[%s]: %d collisions\n",
193  myName, myLock.getCollisions());
194  }
195 
196  bool lock(int ms) { return myLock.lock(ms); }
197 
198  void lock() { myLock.lock(); }
199  bool safeLock() { myLock.lock(); return true; }
200 
201  void unlock() { myLock.unlock(); }
202 
203  bool isLocked() { return myLock.isLocked(); }
204 
206 
207 private:
208  Lock myLock;
209  const char *myName;
210 };
211 
212 // A data-bound lock. The lock excludes access to the given object only,
213 // while allowing other threads to lock different objects.
214 template <class Lock>
216 {
217 public:
218  UT_ObjLockType(int capacity = 256)
219  {
220  myObjs.setCapacity(capacity);
221  }
223  {
224  clear();
225  }
226 
227  int lock(const void *obj)
228  {
229  ut_ObjLockEntry *olock;
230  int ident;
231  {
232  // Lock to access the list. Make sure to grab the olock befrore
233  // the lock is released.
234  typename Lock::Scope lock(myAccessLock);
235  ident = getIdentifier(obj);
236  olock = myObjs[ident];
237  }
238  olock->myLock.lock();
239  return ident;
240  }
241 
242  // Tries to obtain the lock, returns a non-negative identifier if the
243  // lock was obtained - otherwise -1.
244  int tryLock(const void *obj)
245  {
246  ut_ObjLockEntry *olock;
247  int ident;
248  {
249  typename Lock::Scope lock(myAccessLock);
250  ident = getIdentifier(obj);
251  olock = myObjs[ident];
252  }
253  if (olock->myLock.tryLock())
254  return ident;
255  {
256  // Re-lock the list while releasing the object
257  typename Lock::Scope lock(myAccessLock);
258  releaseIdentifier(obj, ident);
259  }
260  return -1;
261  }
262 
263  void unlock(const void *obj, int ident)
264  {
265  typename Lock::Scope lock(myAccessLock);
266  myObjs[ident]->myLock.unlock();
267  releaseIdentifier(obj, ident);
268  }
269 
270 private:
271  void clear()
272  {
273  for (exint i = 0; i < myObjs.entries(); i++)
274  delete myObjs[i];
275  myObjs.entries(0);
276  }
277  int getIdentifier(const void *obj)
278  {
279  exint ident = -1;
280 
281  for (exint i = 0; i < myObjs.entries(); i++)
282  {
283  if (myObjs[i]->myObj == obj)
284  {
285  ident = i;
286  break;
287  }
288  if (!myObjs[i]->myRefCount)
289  ident = i;
290  }
291  if (ident < 0)
292  {
293  myObjs.append(new ut_ObjLockEntry());
294  ident = myObjs.entries()-1;
295  }
296 
297  // Either we found the object or there is an empty slot.
298  myObjs[ident]->myObj = obj;
299  myObjs[ident]->myRefCount++;
300  return ident;
301  }
302  void releaseIdentifier(const void *obj, int ident)
303  {
304  UT_ASSERT(myObjs[ident]->myObj == obj);
305  UT_ASSERT(myObjs[ident]->myRefCount > 0);
306  myObjs[ident]->myRefCount--;
307  if (!myObjs(ident)->myRefCount)
308  myObjs(ident)->myObj = nullptr;
309  }
310 
311  class ut_ObjLockEntry
312  {
313  public:
314  ut_ObjLockEntry()
315  : myObj(nullptr)
316  , myRefCount(0)
317  {
318  }
319 
320  Lock myLock;
321  const void *myObj;
322  int myRefCount;
323  };
324 
325 public:
328 };
329 
330 template <class Lock>
332 {
333 public:
334  UT_AutoObjLockType(UT_ObjLockType<Lock> &lock, const void *obj)
335  : myLock(lock)
336  , myObj(obj)
337  { myId = lock.lock(myObj); }
338 
339  ~UT_AutoObjLockType() { myLock.unlock(myObj, myId); }
340 
341 private:
342  UT_ObjLockType<Lock> &myLock;
343  const void *myObj;
344  int myId;
345 };
346 
347 #endif
348 
UT_AutoLockType(Lock &lock)
Definition: UT_LockUtil.h:29
bool lock(int)
Definition: UT_LockUtil.h:168
GLuint const GLchar * name
Definition: glew.h:1814
void fprintf(FILE *file, const char *fmt, const Args &...args)
Definition: strutil.h:227
UT_DebugLockType(const UT_DebugLockType &rhs)
Definition: UT_LockUtil.h:186
GLhandleARB obj
Definition: glew.h:6236
bool safeLock(Lock &mutex)
Definition: UT_LockUtil.h:130
UT_DebugLockType(const char *name, int lockstate=0, bool threadlock=false)
Definition: UT_LockUtil.h:182
UT_NullLock(int=0)
Definition: UT_LockUtil.h:166
int tryLock(const void *obj)
Definition: UT_LockUtil.h:244
bool safeLock()
Definition: UT_LockUtil.h:169
UT_LockScopeType()=delete
void setCapacity(exint newcapacity)
Definition: UT_ArrayImpl.h:756
UT_LockScopeType(UT_EmptyScope)
Construct without mutex. Use acquire() to give it a mutex.
Definition: UT_LockUtil.h:55
UT_LockScopeType< UT_DebugLockType< Lock > > Scope
Definition: UT_LockUtil.h:205
int64 exint
Definition: SYS_Types.h:120
UT_LockScopeType< UT_NullLock > Scope
Definition: UT_LockUtil.h:174
UT_ObjLockType(int capacity=256)
Definition: UT_LockUtil.h:218
void lock(Lock &mutex)
Definition: UT_LockUtil.h:96
void unlock()
Definition: UT_LockUtil.h:171
UT_LockScopeType(UT_LockScopeType< Lock > &&scope)
Definition: UT_LockUtil.h:69
UT_Array< ut_ObjLockEntry * > myObjs
Definition: UT_LockUtil.h:327
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:453
void lock()
Lock the mutex.
Definition: UT_LockUtil.h:83
bool isLocked()
Definition: UT_LockUtil.h:172
int lock(const void *obj)
Definition: UT_LockUtil.h:227
void lock()
Definition: UT_LockUtil.h:170
exint append(void)
Definition: UT_Array.h:95
UT_AutoObjLockType(UT_ObjLockType< Lock > &lock, const void *obj)
Definition: UT_LockUtil.h:334
bool tryLock()
Try to lock the mutex, return immediately false if someone else owns it.
Definition: UT_LockUtil.h:106
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:135
void unlock(const void *obj, int ident)
Definition: UT_LockUtil.h:263
void unlock()
Unlock the mutex before this object is deleted.
Definition: UT_LockUtil.h:144
bool lock(int ms)
Definition: UT_LockUtil.h:196
UT_LockScopeType(Lock &mutex, bool acquire=true)
Construct with given mutex. Acquires it by default.
Definition: UT_LockUtil.h:62
bool tryLock(Lock &mutex)
Definition: UT_LockUtil.h:119