VirtualBox

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

Last change on this file since 8170 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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