VirtualBox

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

Last change on this file since 22266 was 20932, checked in by vboxsync, 15 years ago

Main/AutoLock: eliminate not entirely working custom windows implementation. IPRT based one works.

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