VirtualBox

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

Last change on this file since 8455 was 8421, checked in by vboxsync, 17 years ago

Main/AutoLock: Distinguish between the number of normal read locks and read locks nested into a write lock (to make sure the reader wait sempahore is properly reset before waiting on it).

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