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