VirtualBox

source: vbox/trunk/src/VBox/Main/include/AutoLock.h@ 8971

Last change on this file since 8971 was 8639, checked in by vboxsync, 17 years ago

Main/AutoLock: More debugging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.2 KB
Line 
1/** @file
2 *
3 * AutoWriteLock/AutoReadLock: smart R/W semaphore wrappers
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#ifndef ____H_AUTOLOCK
23#define ____H_AUTOLOCK
24
25#include <iprt/cdefs.h>
26#include <iprt/types.h>
27#include <iprt/critsect.h>
28#include <iprt/thread.h>
29#include <iprt/semaphore.h>
30
31#include <iprt/err.h>
32#include <iprt/assert.h>
33
34#if defined(DEBUG)
35# include <iprt/asm.h> // for ASMReturnAddress
36#endif
37
38#ifdef VBOX_MAIN_USE_SEMRW
39# include <iprt/semaphore.h>
40#else
41# ifdef VBOX_MAIN_AUTOLOCK_TRAP
42# include <map>
43# include <list>
44# include <string>
45# endif
46#endif
47
48namespace util
49{
50
51#ifdef VBOX_MAIN_AUTOLOCK_TRAP
52namespace internal
53{
54 struct TLS;
55 DECLCALLBACK(void) TLSDestructor (void *aValue);
56}
57#endif /* VBOX_MAIN_AUTOLOCK_TRAP */
58
59/**
60 * Abstract lock operations. See LockHandle and AutoWriteLock for details.
61 */
62class LockOps
63{
64public:
65
66 virtual ~LockOps() {}
67
68 virtual void lock() = 0;
69 virtual void unlock() = 0;
70};
71
72/**
73 * Read lock operations. See LockHandle and AutoWriteLock for details.
74 */
75class ReadLockOps : public LockOps
76{
77public:
78
79 /**
80 * Requests a read (shared) lock.
81 */
82 virtual void lockRead() = 0;
83
84 /**
85 * Releases a read (shared) lock ackquired by lockRead().
86 */
87 virtual void unlockRead() = 0;
88
89 // LockOps interface
90 void lock() { lockRead(); }
91 void unlock() { unlockRead(); }
92};
93
94/**
95 * Write lock operations. See LockHandle and AutoWriteLock for details.
96 */
97class WriteLockOps : public LockOps
98{
99public:
100
101 /**
102 * Requests a write (exclusive) lock.
103 */
104 virtual void lockWrite() = 0;
105
106 /**
107 * Releases a write (exclusive) lock ackquired by lockWrite().
108 */
109 virtual void unlockWrite() = 0;
110
111 // LockOps interface
112 void lock() { lockWrite(); }
113 void unlock() { unlockWrite(); }
114};
115
116/**
117 * Abstract read/write semaphore handle.
118 *
119 * This is a base class to implement semaphores that provide read/write locking.
120 * Subclasses must implement all pure virtual methods of this class together
121 * with pure methods of ReadLockOps and WriteLockOps classes.
122 *
123 * See the AutoWriteLock class documentation for the detailed description of
124 * read and write locks.
125 */
126class LockHandle : protected ReadLockOps, protected WriteLockOps
127{
128public:
129
130 LockHandle() {}
131 virtual ~LockHandle() {}
132
133 /**
134 * Returns @c true if the current thread holds a write lock on this
135 * read/write semaphore. Intended for debugging only.
136 */
137 virtual bool isWriteLockOnCurrentThread() const = 0;
138
139 /**
140 * Returns the current write lock level of this semaphore. The lock level
141 * determines the number of nested #lock() calls on the given semaphore
142 * handle.
143 *
144 * Note that this call is valid only when the current thread owns a write
145 * lock on the given semaphore handle and will assert otherwise.
146 */
147 virtual uint32_t writeLockLevel() const = 0;
148
149 /**
150 * Returns an interface to read lock operations of this semaphore.
151 * Used by constructors of AutoMultiLockN classes.
152 */
153 LockOps *rlock() { return (ReadLockOps *) this; }
154
155 /**
156 * Returns an interface to write lock operations of this semaphore.
157 * Used by constructors of AutoMultiLockN classes.
158 */
159 LockOps *wlock() { return (WriteLockOps *) this; }
160
161private:
162
163 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (LockHandle)
164
165 friend class AutoWriteLock;
166 friend class AutoReadLock;
167};
168
169/**
170 * Full-featured read/write semaphore handle implementation.
171 *
172 * This is an auxiliary base class for classes that need full-featured
173 * read/write locking as described in the AutoWriteLock class documentation.
174 * Instances of classes inherited from this class can be passed as arguments to
175 * the AutoWriteLock and AutoReadLock constructors.
176 */
177class RWLockHandle : public LockHandle
178{
179public:
180
181 RWLockHandle();
182 virtual ~RWLockHandle();
183
184 bool isWriteLockOnCurrentThread() const;
185
186private:
187
188 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (RWLockHandle)
189
190 void lockWrite();
191 void unlockWrite();
192 void lockRead();
193 void unlockRead();
194
195 uint32_t writeLockLevel() const;
196
197#ifdef VBOX_MAIN_USE_SEMRW
198
199 RTSEMRW mSemRW;
200
201#else /* VBOX_MAIN_USE_SEMRW */
202
203 mutable RTCRITSECT mCritSect;
204 RTSEMEVENT mGoWriteSem;
205 RTSEMEVENTMULTI mGoReadSem;
206
207 RTNATIVETHREAD mWriteLockThread;
208
209 uint32_t mReadLockCount; /*< Number of read locks */
210 uint32_t mSelfReadLockCount; /*< Number of read locks nested in write lock */
211
212 uint32_t mWriteLockLevel;
213 uint32_t mWriteLockPending;
214
215# ifdef VBOX_MAIN_AUTOLOCK_TRAP
216
217 enum Operation { LockRead, UnlockRead, LockWrite, UnlockWrite };
218 void logOp (Operation aOp);
219 void gatherInfo (std::string &aInfo);
220
221 friend DECLCALLBACK(void) internal::TLSDestructor (void *aValue);
222 static void TLSDestructor (internal::TLS *aTLS);
223
224 typedef std::list <std::string> ReaderInfo;
225 ReaderInfo mReaderInfo;
226 std::string mWriterInfo;
227
228# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
229
230#endif /* VBOX_MAIN_USE_SEMRW */
231};
232
233/**
234 * Write-only semaphore handle implementation.
235 *
236 * This is an auxiliary base class for classes that need write-only (exclusive)
237 * locking and do not need read (shared) locking. This implementation uses a
238 * cheap and fast critical section for both lockWrite() and lockRead() methods
239 * which makes a lockRead() call fully equivalent to the lockWrite() call and
240 * therefore makes it pointless to use instahces of this class with
241 * AutoReadLock instances -- shared locking will not be possible anyway and
242 * any call to lock() will block if there are lock owners on other threads.
243 *
244 * Use with care only when absolutely sure that shared locks are not necessary.
245 */
246class WriteLockHandle : public LockHandle
247{
248public:
249
250 WriteLockHandle()
251 {
252 RTCritSectInit (&mCritSect);
253 }
254
255 virtual ~WriteLockHandle()
256 {
257 RTCritSectDelete (&mCritSect);
258 }
259
260 bool isWriteLockOnCurrentThread() const
261 {
262 return RTCritSectIsOwner (&mCritSect);
263 }
264
265private:
266
267 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (WriteLockHandle)
268
269 void lockWrite()
270 {
271#if defined(DEBUG)
272 RTCritSectEnterDebug (&mCritSect,
273 "WriteLockHandle::lockWrite() return address >>>",
274 0, (RTUINTPTR) ASMReturnAddress());
275#else
276 RTCritSectEnter (&mCritSect);
277#endif
278 }
279
280 void unlockWrite()
281 {
282 RTCritSectLeave (&mCritSect);
283 }
284
285 void lockRead() { lockWrite(); }
286 void unlockRead() { unlockWrite(); }
287
288 uint32_t writeLockLevel() const
289 {
290 return RTCritSectGetRecursion (&mCritSect);
291 }
292
293 mutable RTCRITSECT mCritSect;
294};
295
296/**
297 * Lockable interface.
298 *
299 * This is an abstract base for classes that need read/write locking. Unlike
300 * RWLockHandle and other classes that makes the read/write semaphore a part of
301 * class data, this class allows subclasses to decide which semaphore handle to
302 * use.
303 */
304class Lockable
305{
306public:
307
308 /**
309 * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock
310 * for locking. Subclasses are allowed to return @c NULL -- in this case,
311 * the AutoWriteLock/AutoReadLock object constructed using an instance of
312 * such subclass will simply turn into no-op.
313 */
314 virtual LockHandle *lockHandle() const = 0;
315
316 /**
317 * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>.
318 * Returns @c false if lockHandle() returns @c NULL.
319 */
320 bool isWriteLockOnCurrentThread()
321 {
322 LockHandle *h = lockHandle();
323 return h ? h->isWriteLockOnCurrentThread() : false;
324 }
325
326 /**
327 * Equivalent to <tt>#lockHandle()->rlock()</tt>.
328 * Returns @c NULL false if lockHandle() returns @c NULL.
329 */
330 LockOps *rlock()
331 {
332 LockHandle *h = lockHandle();
333 return h ? h->rlock() : NULL;
334 }
335
336 /**
337 * Equivalent to <tt>#lockHandle()->wlock()</tt>. Returns @c NULL false if
338 * lockHandle() returns @c NULL.
339 */
340 LockOps *wlock()
341 {
342 LockHandle *h = lockHandle();
343 return h ? h->wlock() : NULL;
344 }
345};
346
347/**
348 * Provides safe management of read/write semaphores in write mode.
349 *
350 * A read/write semaphore is represented by the LockHandle class. This semaphore
351 * can be requested ("locked") in two different modes: for reading and for
352 * writing. A write lock is exclusive and acts like a mutex: only one thread can
353 * acquire a write lock on the given semaphore at a time; all other threads
354 * trying to request a write lock or a read lock (see below) on the same
355 * semaphore will be indefinitely blocked until the owning thread releases the
356 * write lock.
357 *
358 * A read lock is shared. This means that several threads can acquire a read
359 * lock on the same semaphore at the same time provided that there is no thread
360 * that holds a write lock on that semaphore. Note that when there are one or
361 * more threads holding read locks, a request for a write lock on another thread
362 * will be indefinitely blocked until all threads holding read locks release
363 * them.
364 *
365 * Note that write locks can be nested -- the same thread can request a write
366 * lock on the same semaphore several times. In this case, the corresponding
367 * number of release calls must be done in order to completely release all
368 * nested write locks and make the semaphore available for locking by other
369 * threads.
370 *
371 * Read locks can be nested too in which case the same rule of the equal number
372 * of the release calls applies. Read locks can be also nested into write
373 * locks which means that the same thread can successfully request a read lock
374 * if it already holds a write lock. However, please note that the opposite is
375 * <b>not possible</b>: if a thread tries to request a write lock on the same
376 * semaphore it is already holding a read lock, it will definitely produce a
377 * <b>deadlock</b> (i.e. it will block forever waiting for itself).
378 *
379 * Note that instances of the AutoWriteLock class manage write locks of
380 * read/write semaphores only. In order to manage read locks, please use the
381 * AutoReadLock class.
382 *
383 * Safe semaphore management consists of the following:
384 * <ul>
385 * <li>When an instance of the AutoWriteLock class is constructed given a
386 * valid semaphore handle, it will automatically request a write lock on that
387 * semaphore.
388 * </li>
389 * <li>When an instance of the AutoWriteLock class constructed given a valid
390 * semaphore handle is destroyed (e.g. goes out of scope), it will
391 * automatically release the write lock that was requested upon construction
392 * and also all nested write locks requested later using the #lock() call
393 * (note that the latter is considered to be a program logic error, see the
394 * #~AutoWriteLock() description for details).
395 * </li>
396 * </ul>
397 *
398 * Note that the LockHandle class taken by AutoWriteLock constructors is an
399 * abstract base of the read/write semaphore. You should choose one of the
400 * existing subclasses of this abstract class or create your own subclass that
401 * implements necessary read and write lock semantics. The most suitable choice
402 * is the RWLockHandle class which provides full support for both read and write
403 * locks as describerd above. Alternatively, you can use the WriteLockHandle
404 * class if you only need write (exclusive) locking (WriteLockHandle requires
405 * less system resources and works faster).
406 *
407 * A typical usage pattern of the AutoWriteLock class is as follows:
408 * <code>
409 * struct Struct : public RWLockHandle
410 * {
411 * ...
412 * };
413 *
414 * void foo (Struct &aStruct)
415 * {
416 * {
417 * // acquire a write lock of aStruct
418 * AutoWriteLock alock (aStruct);
419 *
420 * // now we can modify aStruct in a thread-safe manner
421 * aStruct.foo = ...;
422 *
423 * // note that the write lock will be automatically released upon
424 * // execution of the return statement below
425 * if (!aStruct.bar)
426 * return;
427 *
428 * ...
429 * }
430 *
431 * // note that the write lock is automatically released here
432 * }
433 * </code>
434 *
435 * <b>Locking policy</b>
436 *
437 * When there are multiple threads and multiple objects to lock, there is always
438 * a potential possibility to produce a deadlock if the lock order is mixed up.
439 * Here is a classical example of a deadlock when two threads need to lock the
440 * same two objects in a row but do it in different order:
441 * <code>
442 * Thread 1:
443 * #1: AutoWriteLock (mFoo);
444 * ...
445 * #2: AutoWriteLock (mBar);
446 * ...
447 * Thread 2:
448 * #3: AutoWriteLock (mBar);
449 * ...
450 * #4: AutoWriteLock (mFoo);
451 * ...
452 * </code>
453 *
454 * If the threads happen to be scheduled so that #3 completes after #1 has
455 * completed but before #2 got control, the threads will hit a deadlock: Thread
456 * 2 will be holding mBar and waiting for mFoo at #4 forever because Thread 1 is
457 * holding mFoo and won't release it until it acquires mBar at #2 that will
458 * never happen because mBar is held by Thread 2.
459 *
460 * One of ways to avoid the described behavior is to never lock more than one
461 * obhect in a row. While it is definitely a good and safe practice, it's not
462 * always possible: the application logic may require several simultaneous locks
463 * in order to provide data integrity.
464 *
465 * One of the possibilities to solve the deadlock problem is to make sure that
466 * the locking order is always the same across the application. In the above
467 * example, it would mean that <b>both</b> threads should first requiest a lock
468 * of mFoo and then mBar (or vice versa). One of the methods to guarantee the
469 * locking order consistent is to introduce a set of locking rules. The
470 * advantage of this method is that it doesn't require any special semaphore
471 * implementation or additional control structures. The disadvantage is that
472 * it's the programmer who must make sure these rules are obeyed across the
473 * whole application so the human factor applies. Taking the simplicity of this
474 * method into account, it is chosen to solve potential deadlock problems when
475 * using AutoWriteLock and AutoReadLock classes. Here are the locking rules
476 * that must be obeyed by <b>all</b> users of these classes. Note that if more
477 * than one rule matches the given group of objects to lock, all of these rules
478 * must be met:
479 * <ol>
480 * <li>If there is a parent-child (or master-slave) relationship between the
481 * locked objects, parent (master) objects must be locked before child
482 * (slave) objects.
483 * </li>
484 * <li>When a group of equal objects (in terms of parent-child or
485 * master-slave relationsip) needs to be locked in a raw, the lock order
486 * must match the sort order (which must be consistent for the given group).
487 * </ol>
488 * Note that if there is no pragrammatically expressed sort order (e.g.
489 * the objects are not part of the sorted vector or list but instead are
490 * separate data members of a class), object class names sorted in alphabetical
491 * order must be used to determine the lock order. If there is more than one
492 * object of the given class, the object variable names' alphabetical order must
493 * be used as a lock order. When objects are not represented as individual
494 * variables, as in case of unsorted arrays/lists, the list of alphabetically
495 * sorted object UUIDs must be used to determine the sort order.
496 *
497 * All non-standard locking order must be avoided by all means, but when
498 * absolutely necessary, it must be clearly documented at relevant places so it
499 * is well seen by other developers. For example, if a set of instances of some
500 * class needs to be locked but these instances are not part of the sorted list
501 * and don't have UUIDs, then the class description must state what to use to
502 * determine the lock order (maybe some property that returns an unique value
503 * per every object).
504 */
505class AutoWriteLock
506{
507public:
508
509 /**
510 * Constructs a null instance that does not manage any read/write
511 * semaphore.
512 *
513 * Note that all method calls on a null instance are no-ops. This allows to
514 * have the code where lock protection can be selected (or omitted) at
515 * runtime.
516 */
517 AutoWriteLock() : mHandle (NULL), mLockLevel (0), mGlobalLockLevel (0) {}
518
519 /**
520 * Constructs a new instance that will start managing the given read/write
521 * semaphore by requesting a write lock.
522 */
523 AutoWriteLock (LockHandle *aHandle)
524 : mHandle (aHandle), mLockLevel (0), mGlobalLockLevel (0)
525 { lock(); }
526
527 /**
528 * Constructs a new instance that will start managing the given read/write
529 * semaphore by requesting a write lock.
530 */
531 AutoWriteLock (LockHandle &aHandle)
532 : mHandle (&aHandle), mLockLevel (0), mGlobalLockLevel (0)
533 { lock(); }
534
535 /**
536 * Constructs a new instance that will start managing the given read/write
537 * semaphore by requesting a write lock.
538 */
539 AutoWriteLock (const Lockable &aLockable)
540 : mHandle (aLockable.lockHandle()), mLockLevel (0), mGlobalLockLevel (0)
541 { lock(); }
542
543 /**
544 * Constructs a new instance that will start managing the given read/write
545 * semaphore by requesting a write lock.
546 */
547 AutoWriteLock (const Lockable *aLockable)
548 : mHandle (aLockable ? aLockable->lockHandle() : NULL)
549 , mLockLevel (0), mGlobalLockLevel (0)
550 { lock(); }
551
552 /**
553 * Release all write locks acquired by this instance through the #lock()
554 * call and destroys the instance.
555 *
556 * Note that if there there are nested #lock() calls without the
557 * corresponding number of #unlock() calls when the destructor is called, it
558 * will assert. This is because having an unbalanced number of nested locks
559 * is a program logic error which must be fixed.
560 */
561 ~AutoWriteLock()
562 {
563 if (mHandle)
564 {
565 if (mGlobalLockLevel)
566 {
567 mGlobalLockLevel -= mLockLevel;
568 mLockLevel = 0;
569 for (; mGlobalLockLevel; -- mGlobalLockLevel)
570 mHandle->lockWrite();
571 }
572
573 AssertMsg (mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
574 for (; mLockLevel; -- mLockLevel)
575 mHandle->unlockWrite();
576 }
577 }
578
579 /**
580 * Requests a write (exclusive) lock. If a write lock is already owned by
581 * this thread, increases the lock level (allowing for nested write locks on
582 * the same thread). Blocks indefinitely if a write lock or a read lock is
583 * already owned by another thread until that tread releases the locks,
584 * otherwise returns immediately.
585 */
586 void lock()
587 {
588 if (mHandle)
589 {
590 mHandle->lockWrite();
591 ++ mLockLevel;
592 Assert (mLockLevel != 0 /* overflow? */);
593 }
594 }
595
596 /**
597 * Decreases the write lock level increased by #lock(). If the level drops
598 * to zero (e.g. the number of nested #unlock() calls matches the number of
599 * nested #lock() calls), releases the lock making the managed semaphore
600 * available for locking by other threads.
601 */
602 void unlock()
603 {
604 if (mHandle)
605 {
606 AssertReturnVoid (mLockLevel != 0 /* unlock() w/o preceding lock()? */);
607 mHandle->unlockWrite();
608 -- mLockLevel;
609 }
610 }
611
612 /**
613 * Causes the current thread to completely release the write lock to make
614 * the managed semaphore immediately available for locking by other threads.
615 *
616 * This implies that all nested write locks on the semaphore will be
617 * released, even those that were acquired through the calls to #lock()
618 * methods of all other AutoWriteLock/AutoReadLock instances managing the
619 * <b>same</b> read/write semaphore.
620 *
621 * After calling this method, the only method you are allowed to call is
622 * #enter(). It will acquire the write lock again and restore the same
623 * level of nesting as it had before calling #leave().
624 *
625 * If this instance is destroyed without calling #enter(), the destructor
626 * will try to restore the write lock level that existed when #leave() was
627 * called minus the number of nested #lock() calls made on this instance
628 * itself. This is done to preserve lock levels of other
629 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
630 * any). Tiis also means that the destructor may indefinitely block if a
631 * write or a read lock is owned by some other thread by that time.
632 */
633 void leave()
634 {
635 if (mHandle)
636 {
637 AssertReturnVoid (mLockLevel != 0 /* leave() w/o preceding lock()? */);
638 AssertReturnVoid (mGlobalLockLevel == 0 /* second leave() in a row? */);
639
640 mGlobalLockLevel = mHandle->writeLockLevel();
641 AssertReturnVoid (mGlobalLockLevel >= mLockLevel /* logic error! */);
642
643 for (uint32_t left = mGlobalLockLevel; left; -- left)
644 mHandle->unlockWrite();
645 }
646 }
647
648 /**
649 * Causes the current thread to restore the write lock level after the
650 * #leave() call. This call will indefinitely block if another thread has
651 * successfully acquired a write or a read lock on the same semaphore in
652 * between.
653 */
654 void enter()
655 {
656 if (mHandle)
657 {
658 AssertReturnVoid (mLockLevel != 0 /* enter() w/o preceding lock()+leave()? */);
659 AssertReturnVoid (mGlobalLockLevel != 0 /* enter() w/o preceding leave()? */);
660
661 for (; mGlobalLockLevel; -- mGlobalLockLevel)
662 mHandle->lockWrite();
663 }
664 }
665
666 /** Returns @c true if this instance manages a null semaphore handle. */
667 bool isNull() const { return mHandle == NULL; }
668 bool operator !() const { return isNull(); }
669
670 /**
671 * Returns @c true if the current thread holds a write lock on the managed
672 * read/write semaphore. Returns @c false if the managed semaphore is @c
673 * NULL.
674 *
675 * @note Intended for debugging only.
676 */
677 bool isWriteLockOnCurrentThread() const
678 {
679 return mHandle ? mHandle->isWriteLockOnCurrentThread() : false;
680 }
681
682 /**
683 * Returns the current write lock level of the managed smaphore. The lock
684 * level determines the number of nested #lock() calls on the given
685 * semaphore handle. Returns @c 0 if the managed semaphore is @c
686 * NULL.
687 *
688 * Note that this call is valid only when the current thread owns a write
689 * lock on the given semaphore handle and will assert otherwise.
690 *
691 * @note Intended for debugging only.
692 */
693 uint32_t writeLockLevel() const
694 {
695 return mHandle ? mHandle->writeLockLevel() : 0;
696 }
697
698 /**
699 * Returns @c true if this instance manages the given semaphore handle.
700 *
701 * @note Intended for debugging only.
702 */
703 bool belongsTo (const LockHandle &aHandle) const { return mHandle == &aHandle; }
704
705 /**
706 * Returns @c true if this instance manages the given semaphore handle.
707 *
708 * @note Intended for debugging only.
709 */
710 bool belongsTo (const LockHandle *aHandle) const { return mHandle == aHandle; }
711
712 /**
713 * Returns @c true if this instance manages the given lockable object.
714 *
715 * @note Intended for debugging only.
716 */
717 bool belongsTo (const Lockable &aLockable)
718 {
719 return belongsTo (aLockable.lockHandle());
720 }
721
722 /**
723 * Returns @c true if this instance manages the given lockable object.
724 *
725 * @note Intended for debugging only.
726 */
727 bool belongsTo (const Lockable *aLockable)
728 {
729 return aLockable && belongsTo (aLockable->lockHandle());
730 }
731
732private:
733
734 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoWriteLock)
735 DECLARE_CLS_NEW_DELETE_NOOP (AutoWriteLock)
736
737 LockHandle *mHandle;
738 uint32_t mLockLevel;
739 uint32_t mGlobalLockLevel;
740
741 template <size_t> friend class AutoMultiWriteLockBase;
742};
743
744////////////////////////////////////////////////////////////////////////////////
745
746/**
747 * Provides safe management of read/write semaphores in read mode.
748 *
749 * This class differs from the AutoWriteLock class is so that it's #lock() and
750 * #unlock() methods requests and release read (shared) locks on the managed
751 * read/write semaphore instead of write (exclusive) locks. See the
752 * AutoWriteLock class description for more information about read and write
753 * locks.
754 *
755 * Safe semaphore management consists of the following:
756 * <ul>
757 * <li>When an instance of the AutoReadLock class is constructed given a
758 * valid semaphore handle, it will automatically request a read lock on that
759 * semaphore.
760 * </li>
761 * <li>When an instance of the AutoReadLock class constructed given a valid
762 * semaphore handle is destroyed (e.g. goes out of scope), it will
763 * automatically release the read lock that was requested upon construction
764 * and also all nested read locks requested later using the #lock() call (note
765 * that the latter is considered to be a program logic error, see the
766 * #~AutoReadLock() description for details).
767 * </li>
768 * </ul>
769 *
770 * Note that the LockHandle class taken by AutoReadLock constructors is an
771 * abstract base of the read/write semaphore. You should choose one of the
772 * existing subclasses of this abstract class or create your own subclass that
773 * implements necessary read and write lock semantics. The most suitable choice
774 * is the RWLockHandle class which provides full support for both read and write
775 * locks as describerd in AutoWriteLock docs. Alternatively, you can use the
776 * WriteLockHandle class if you only need write (exclusive) locking
777 * (WriteLockHandle requires less system resources and works faster).
778 *
779 * However, please note that it absolutely does not make sense to manage
780 * WriteLockHandle semaphores with AutoReadLock instances because
781 * AutoReadLock instances will behave like AutoWriteLock instances in this
782 * case since WriteLockHandle provides only exclusive write locking. You have
783 * been warned.
784
785 * A typical usage pattern of the AutoReadLock class is as follows:
786 * <code>
787 * struct Struct : public RWLockHandle
788 * {
789 * ...
790 * };
791 *
792 * void foo (Struct &aStruct)
793 * {
794 * {
795 * // acquire a read lock of aStruct (note that two foo() calls may be
796 * executed on separate threads simultaneously w/o blocking each other)
797 * AutoReadLock alock (aStruct);
798 *
799 * // now we can read aStruct in a thread-safe manner
800 * if (aStruct.foo)
801 * ...;
802 *
803 * // note that the read lock will be automatically released upon
804 * // execution of the return statement below
805 * if (!aStruct.bar)
806 * return;
807 *
808 * ...
809 * }
810 *
811 * // note that the read lock is automatically released here
812 * }
813 * </code>
814 */
815class AutoReadLock
816{
817public:
818
819 /**
820 * Constructs a null instance that does not manage any read/write
821 * semaphore.
822 *
823 * Note that all method calls on a null instance are no-ops. This allows to
824 * have the code where lock protection can be selected (or omitted) at
825 * runtime.
826 */
827 AutoReadLock() : mHandle (NULL), mLockLevel (0) {}
828
829 /**
830 * Constructs a new instance that will start managing the given read/write
831 * semaphore by requesting a read lock.
832 */
833 AutoReadLock (LockHandle *aHandle)
834 : mHandle (aHandle), mLockLevel (0)
835 { lock(); }
836
837 /**
838 * Constructs a new instance that will start managing the given read/write
839 * semaphore by requesting a read lock.
840 */
841 AutoReadLock (LockHandle &aHandle)
842 : mHandle (&aHandle), mLockLevel (0)
843 { lock(); }
844
845 /**
846 * Constructs a new instance that will start managing the given read/write
847 * semaphore by requesting a read lock.
848 */
849 AutoReadLock (const Lockable &aLockable)
850 : mHandle (aLockable.lockHandle()), mLockLevel (0)
851 { lock(); }
852
853 /**
854 * Constructs a new instance that will start managing the given read/write
855 * semaphore by requesting a read lock.
856 */
857 AutoReadLock (const Lockable *aLockable)
858 : mHandle (aLockable ? aLockable->lockHandle() : NULL)
859 , mLockLevel (0)
860 { lock(); }
861
862 /**
863 * Release all read locks acquired by this instance through the #lock()
864 * call and destroys the instance.
865 *
866 * Note that if there there are nested #lock() calls without the
867 * corresponding number of #unlock() calls when the destructor is called, it
868 * will assert. This is because having an unbalanced number of nested locks
869 * is a program logic error which must be fixed.
870 */
871 ~AutoReadLock()
872 {
873 if (mHandle)
874 {
875 AssertMsg (mLockLevel <= 1, ("Lock level > 1: %d\n", mLockLevel));
876 for (; mLockLevel; -- mLockLevel)
877 mHandle->unlockRead();
878 }
879 }
880
881 /**
882 * Requests a read (shared) lock. If a read lock is already owned by
883 * this thread, increases the lock level (allowing for nested read locks on
884 * the same thread). Blocks indefinitely if a write lock is already owned by
885 * another thread until that tread releases the write lock, otherwise
886 * returns immediately.
887 *
888 * Note that this method returns immediately even if any number of other
889 * threads owns read locks on the same semaphore. Also returns immediately
890 * if a write lock on this semaphore is owned by the current thread which
891 * allows for read locks nested into write locks on the same thread.
892 */
893 void lock()
894 {
895 if (mHandle)
896 {
897 mHandle->lockRead();
898 ++ mLockLevel;
899 Assert (mLockLevel != 0 /* overflow? */);
900 }
901 }
902
903 /**
904 * Decreases the read lock level increased by #lock(). If the level drops to
905 * zero (e.g. the number of nested #unlock() calls matches the number of
906 * nested #lock() calls), releases the lock making the managed semaphore
907 * available for locking by other threads.
908 */
909 void unlock()
910 {
911 if (mHandle)
912 {
913 AssertReturnVoid (mLockLevel != 0 /* unlock() w/o preceding lock()? */);
914 mHandle->unlockRead();
915 -- mLockLevel;
916 }
917 }
918
919 /** Returns @c true if this instance manages a null semaphore handle. */
920 bool isNull() const { return mHandle == NULL; }
921 bool operator !() const { return isNull(); }
922
923 /**
924 * Returns @c true if this instance manages the given semaphore handle.
925 *
926 * @note Intended for debugging only.
927 */
928 bool belongsTo (const LockHandle &aHandle) const { return mHandle == &aHandle; }
929
930 /**
931 * Returns @c true if this instance manages the given semaphore handle.
932 *
933 * @note Intended for debugging only.
934 */
935 bool belongsTo (const LockHandle *aHandle) const { return mHandle == aHandle; }
936
937 /**
938 * Returns @c true if this instance manages the given lockable object.
939 *
940 * @note Intended for debugging only.
941 */
942 bool belongsTo (const Lockable &aLockable)
943 {
944 return belongsTo (aLockable.lockHandle());
945 }
946
947 /**
948 * Returns @c true if this instance manages the given lockable object.
949 *
950 * @note Intended for debugging only.
951 */
952 bool belongsTo (const Lockable *aLockable)
953 {
954 return aLockable && belongsTo (aLockable->lockHandle());
955 }
956
957private:
958
959 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoReadLock)
960 DECLARE_CLS_NEW_DELETE_NOOP (AutoReadLock)
961
962 LockHandle *mHandle;
963 uint32_t mLockLevel;
964};
965
966////////////////////////////////////////////////////////////////////////////////
967
968/**
969 * Helper template class for AutoMultiLockN classes.
970 *
971 * @param Cnt number of read/write semaphores to manage.
972 */
973template <size_t Cnt>
974class AutoMultiLockBase
975{
976public:
977
978 /**
979 * Releases all locks if not yet released by #unlock() and destroys the
980 * instance.
981 */
982 ~AutoMultiLockBase()
983 {
984 if (mIsLocked)
985 unlock();
986 }
987
988 /**
989 * Calls LockOps::lock() methods of all managed semaphore handles
990 * in order they were passed to the constructor.
991 *
992 * Note that as opposed to LockHandle::lock(), this call cannot be nested
993 * and will assert if so.
994 */
995 void lock()
996 {
997 AssertReturnVoid (!mIsLocked);
998
999 size_t i = 0;
1000 while (i < ELEMENTS (mOps))
1001 if (mOps [i])
1002 mOps [i ++]->lock();
1003 mIsLocked = true;
1004 }
1005
1006 /**
1007 * Calls LockOps::unlock() methods of all managed semaphore handles in
1008 * reverse to the order they were passed to the constructor.
1009 *
1010 * Note that as opposed to LockHandle::unlock(), this call cannot be nested
1011 * and will assert if so.
1012 */
1013 void unlock()
1014 {
1015 AssertReturnVoid (mIsLocked);
1016
1017 AssertReturnVoid (ELEMENTS (mOps) > 0);
1018 size_t i = ELEMENTS (mOps);
1019 do
1020 if (mOps [-- i])
1021 mOps [i]->unlock();
1022 while (i != 0);
1023 mIsLocked = false;
1024 }
1025
1026protected:
1027
1028 AutoMultiLockBase() : mIsLocked (false) {}
1029
1030 LockOps *mOps [Cnt];
1031 bool mIsLocked;
1032
1033private:
1034
1035 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiLockBase)
1036 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiLockBase)
1037};
1038
1039/** AutoMultiLockBase <0> is meaningless and forbidden. */
1040template<>
1041class AutoMultiLockBase <0> { private : AutoMultiLockBase(); };
1042
1043/** AutoMultiLockBase <1> is meaningless and forbidden. */
1044template<>
1045class AutoMultiLockBase <1> { private : AutoMultiLockBase(); };
1046
1047////////////////////////////////////////////////////////////////////////////////
1048
1049/* AutoMultiLockN class definitions */
1050
1051#define A(n) LockOps *l##n
1052#define B(n) mOps [n] = l##n
1053
1054/**
1055 * AutoMultiLock for 2 locks.
1056 *
1057 * The AutoMultiLockN family of classes provides a possibility to manage several
1058 * read/write semaphores at once. This is handy if all managed semaphores need
1059 * to be locked and unlocked synchronously and will also help to avoid locking
1060 * order errors.
1061 *
1062 * Instances of AutoMultiLockN classes are constructed from a list of LockOps
1063 * arguments. The AutoMultiLockBase::lock() method will make sure that the given
1064 * list of semaphores represented by LockOps pointers will be locked in order
1065 * they are passed to the constructor. The AutoMultiLockBase::unlock() method
1066 * will make sure that they will be unlocked in reverse order.
1067 *
1068 * The type of the lock to request is specified for each semaphore individually
1069 * using the corresponding LockOps getter of a LockHandle or Lockable object:
1070 * LockHandle::wlock() in order to request a write lock or LockHandle::rlock()
1071 * in order to request a read lock.
1072 *
1073 * Here is a typical usage pattern:
1074 * <code>
1075 * ...
1076 * LockHandle data1, data2;
1077 * ...
1078 * {
1079 * AutoMultiLock2 multiLock (data1.wlock(), data2.rlock());
1080 * // both locks are held here:
1081 * // - data1 is locked in write mode (like AutoWriteLock)
1082 * // - data2 is locked in read mode (like AutoReadLock)
1083 * }
1084 * // both locks are released here
1085 * </code>
1086 */
1087class AutoMultiLock2 : public AutoMultiLockBase <2>
1088{
1089public:
1090 AutoMultiLock2 (A(0), A(1))
1091 { B(0); B(1); lock(); }
1092};
1093
1094/** AutoMultiLock for 3 locks. See AutoMultiLock2 for more information. */
1095class AutoMultiLock3 : public AutoMultiLockBase <3>
1096{
1097public:
1098 AutoMultiLock3 (A(0), A(1), A(2))
1099 { B(0); B(1); B(2); lock(); }
1100};
1101
1102/** AutoMultiLock for 4 locks. See AutoMultiLock2 for more information. */
1103class AutoMultiLock4 : public AutoMultiLockBase <4>
1104{
1105public:
1106 AutoMultiLock4 (A(0), A(1), A(2), A(3))
1107 { B(0); B(1); B(2); B(3); lock(); }
1108};
1109
1110#undef B
1111#undef A
1112
1113////////////////////////////////////////////////////////////////////////////////
1114
1115/**
1116 * Helper template class for AutoMultiWriteLockN classes.
1117 *
1118 * @param Cnt number of write semaphores to manage.
1119 */
1120template <size_t Cnt>
1121class AutoMultiWriteLockBase
1122{
1123public:
1124
1125 /**
1126 * Calls AutoWriteLock::lock() methods for all managed semaphore handles in
1127 * order they were passed to the constructor.
1128 */
1129 void lock()
1130 {
1131 size_t i = 0;
1132 while (i < ELEMENTS (mLocks))
1133 mLocks [i ++].lock();
1134 }
1135
1136 /**
1137 * Calls AutoWriteLock::unlock() methods for all managed semaphore handles
1138 * in reverse to the order they were passed to the constructor.
1139 */
1140 void unlock()
1141 {
1142 AssertReturnVoid (ELEMENTS (mLocks) > 0);
1143 size_t i = ELEMENTS (mLocks);
1144 do
1145 mLocks [-- i].unlock();
1146 while (i != 0);
1147 }
1148
1149 /**
1150 * Calls AutoWriteLock::leave() methods for all managed semaphore handles in
1151 * reverse to the order they were passed to the constructor.
1152 */
1153 void leave()
1154 {
1155 AssertReturnVoid (ELEMENTS (mLocks) > 0);
1156 size_t i = ELEMENTS (mLocks);
1157 do
1158 mLocks [-- i].leave();
1159 while (i != 0);
1160 }
1161
1162 /**
1163 * Calls AutoWriteLock::enter() methods for all managed semaphore handles in
1164 * order they were passed to the constructor.
1165 */
1166 void enter()
1167 {
1168 size_t i = 0;
1169 while (i < ELEMENTS (mLocks))
1170 mLocks [i ++].enter();
1171 }
1172
1173protected:
1174
1175 AutoMultiWriteLockBase() {}
1176
1177 void setLockHandle (size_t aIdx, LockHandle *aHandle)
1178 { mLocks [aIdx].mHandle = aHandle; }
1179
1180private:
1181
1182 AutoWriteLock mLocks [Cnt];
1183
1184 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoMultiWriteLockBase)
1185 DECLARE_CLS_NEW_DELETE_NOOP (AutoMultiWriteLockBase)
1186};
1187
1188/** AutoMultiWriteLockBase <0> is meaningless and forbidden. */
1189template<>
1190class AutoMultiWriteLockBase <0> { private : AutoMultiWriteLockBase(); };
1191
1192/** AutoMultiWriteLockBase <1> is meaningless and forbidden. */
1193template<>
1194class AutoMultiWriteLockBase <1> { private : AutoMultiWriteLockBase(); };
1195
1196////////////////////////////////////////////////////////////////////////////////
1197
1198/* AutoMultiLockN class definitions */
1199
1200#define A(n) LockHandle *l##n
1201#define B(n) setLockHandle (n, l##n)
1202
1203#define C(n) Lockable *l##n
1204#define D(n) setLockHandle (n, l##n ? l##n->lockHandle() : NULL)
1205
1206/**
1207 * AutoMultiWriteLock for 2 locks.
1208 *
1209 * The AutoMultiWriteLockN family of classes provides a possibility to manage
1210 * several read/write semaphores at once. This is handy if all managed
1211 * semaphores need to be locked and unlocked synchronously and will also help to
1212 * avoid locking order errors.
1213 *
1214 * The functionality of the AutoMultiWriteLockN class family is similar to the
1215 * functionality of the AutoMultiLockN class family (see the AutoMultiLock2
1216 * class for details) with two important differences:
1217 * <ol>
1218 * <li>Instances of AutoMultiWriteLockN classes are constructed from a list
1219 * of LockHandle or Lockable arguments directly instead of getting
1220 * intermediate LockOps interface pointers.
1221 * </li>
1222 * <li>All locks are requested in <b>write</b> mode.
1223 * </li>
1224 * <li>Since all locks are requested in write mode, bulk
1225 * AutoMultiWriteLockBase::leave() and AutoMultiWriteLockBase::enter()
1226 * operations are also available, that will leave and enter all managed
1227 * semaphores at once in the proper order (similarly to
1228 * AutoMultiWriteLockBase::lock() and AutoMultiWriteLockBase::unlock()).
1229 * </li>
1230 * </ol>
1231 *
1232 * Here is a typical usage pattern:
1233 * <code>
1234 * ...
1235 * LockHandle data1, data2;
1236 * ...
1237 * {
1238 * AutoMultiWriteLock2 multiLock (&data1, &data2);
1239 * // both locks are held in write mode here
1240 * }
1241 * // both locks are released here
1242 * </code>
1243 */
1244class AutoMultiWriteLock2 : public AutoMultiWriteLockBase <2>
1245{
1246public:
1247 AutoMultiWriteLock2 (A(0), A(1))
1248 { B(0); B(1); lock(); }
1249 AutoMultiWriteLock2 (C(0), C(1))
1250 { D(0); D(1); lock(); }
1251};
1252
1253/** AutoMultiWriteLock for 3 locks. See AutoMultiWriteLock2 for more details. */
1254class AutoMultiWriteLock3 : public AutoMultiWriteLockBase <3>
1255{
1256public:
1257 AutoMultiWriteLock3 (A(0), A(1), A(2))
1258 { B(0); B(1); B(2); lock(); }
1259 AutoMultiWriteLock3 (C(0), C(1), C(2))
1260 { D(0); D(1); D(2); lock(); }
1261};
1262
1263/** AutoMultiWriteLock for 4 locks. See AutoMultiWriteLock2 for more details. */
1264class AutoMultiWriteLock4 : public AutoMultiWriteLockBase <4>
1265{
1266public:
1267 AutoMultiWriteLock4 (A(0), A(1), A(2), A(3))
1268 { B(0); B(1); B(2); B(3); lock(); }
1269 AutoMultiWriteLock4 (C(0), C(1), C(2), C(3))
1270 { D(0); D(1); D(2); D(3); lock(); }
1271};
1272
1273#undef D
1274#undef C
1275#undef B
1276#undef A
1277
1278} /* namespace util */
1279
1280#endif // ____H_AUTOLOCK
1281
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette