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