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