VirtualBox

source: vbox/trunk/include/VBox/com/AutoLock.h@ 67721

Last change on this file since 67721 was 67721, checked in by vboxsync, 7 years ago

glue/AutoLock: Adding isReadLockedOnCurrentThread method for debug assertions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.1 KB
Line 
1/** @file
2 * MS COM / XPCOM Abstraction Layer - Automatic locks, implementation.
3 */
4
5/*
6 * Copyright (C) 2006-2016 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * The contents of this file may alternatively be used under the terms
17 * of the Common Development and Distribution License Version 1.0
18 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19 * VirtualBox OSE distribution, in which case the provisions of the
20 * CDDL are applicable instead of those of the GPL.
21 *
22 * You may elect to license modified versions of this file under the
23 * terms and conditions of either the GPL or the CDDL or both.
24 */
25
26#ifndef ___VBox_com_AutoLock_h
27#define ___VBox_com_AutoLock_h
28
29#include <iprt/types.h>
30
31
32/** @defgroup grp_com_autolock Automatic Locks
33 * @ingroup grp_com
34 * @{
35 */
36
37// macros for automatic lock validation; these will amount to nothing
38// unless lock validation is enabled for the runtime
39#if defined(RT_LOCK_STRICT)
40# define VBOX_WITH_MAIN_LOCK_VALIDATION
41# define COMMA_LOCKVAL_SRC_POS , RT_SRC_POS
42# define LOCKVAL_SRC_POS_DECL RT_SRC_POS_DECL
43# define COMMA_LOCKVAL_SRC_POS_DECL , RT_SRC_POS_DECL
44# define LOCKVAL_SRC_POS_ARGS RT_SRC_POS_ARGS
45# define COMMA_LOCKVAL_SRC_POS_ARGS , RT_SRC_POS_ARGS
46#else
47# define COMMA_LOCKVAL_SRC_POS
48# define LOCKVAL_SRC_POS_DECL
49# define COMMA_LOCKVAL_SRC_POS_DECL
50# define LOCKVAL_SRC_POS_ARGS
51# define COMMA_LOCKVAL_SRC_POS_ARGS
52#endif
53
54namespace util
55{
56
57////////////////////////////////////////////////////////////////////////////////
58//
59// Order classes for lock validation
60//
61////////////////////////////////////////////////////////////////////////////////
62
63/**
64 * IPRT now has a sophisticated system of run-time locking classes to validate
65 * locking order. Since the Main code is handled by simpler minds, we want
66 * compile-time constants for simplicity, and we'll look up the run-time classes
67 * in AutoLock.cpp transparently. These are passed to the constructors of the
68 * LockHandle classes.
69 */
70enum VBoxLockingClass
71{
72 LOCKCLASS_NONE = 0,
73 LOCKCLASS_WEBSERVICE = 1, // highest order: webservice locks
74 LOCKCLASS_VIRTUALBOXOBJECT = 2, // highest order within Main itself: VirtualBox object lock
75 LOCKCLASS_HOSTOBJECT = 3, // Host object lock
76 LOCKCLASS_LISTOFMACHINES = 4, // list of machines in VirtualBox object
77 LOCKCLASS_MACHINEOBJECT = 5, // Machine object lock
78 LOCKCLASS_SNAPSHOTOBJECT = 6, // snapshot object locks
79 // (the snapshots tree, including the child pointers in Snapshot,
80 // is protected by the normal Machine object lock)
81 LOCKCLASS_MEDIUMQUERY = 7, // lock used to protect Machine::queryInfo
82 LOCKCLASS_LISTOFMEDIA = 8, // list of media (hard disks, DVDs, floppies) in VirtualBox object
83 LOCKCLASS_LISTOFOTHEROBJECTS = 9, // any other list of objects
84 LOCKCLASS_OTHEROBJECT = 10, // any regular object member variable lock
85 LOCKCLASS_PROGRESSLIST = 11, // list of progress objects in VirtualBox; no other object lock
86 // may be held after this!
87 LOCKCLASS_OBJECTSTATE = 12 // object state lock (handled by AutoCaller classes)
88};
89
90void InitAutoLockSystem();
91
92/**
93 * Check whether the current thread holds any locks in the given class
94 *
95 * @return true if any such locks are held, false otherwise. If the lock
96 * validator is not compiled in, always returns false.
97 * @param lockClass Which lock class to check.
98 */
99bool AutoLockHoldsLocksInClass(VBoxLockingClass lockClass);
100
101////////////////////////////////////////////////////////////////////////////////
102//
103// LockHandle and friends
104//
105////////////////////////////////////////////////////////////////////////////////
106
107/**
108 * Abstract base class for semaphore handles (RWLockHandle and WriteLockHandle).
109 * Don't use this directly, but this implements lock validation for them.
110 */
111class LockHandle
112{
113public:
114 LockHandle()
115 {}
116
117 virtual ~LockHandle()
118 {}
119
120 /**
121 * Returns @c true if the current thread holds a write lock on this
122 * read/write semaphore. Intended for debugging only.
123 */
124 virtual bool isWriteLockOnCurrentThread() const = 0;
125
126 /**
127 * Returns @c true if the current thread holds a read lock on this
128 * read/write semaphore. Intended for debugging only as it isn't always
129 * accurate given @a fWannaHear.
130 */
131 virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const = 0;
132
133 /**
134 * Returns the current write lock level of this semaphore. The lock level
135 * determines the number of nested #lockWrite() calls on the given
136 * semaphore handle.
137 *
138 * Note that this call is valid only when the current thread owns a write
139 * lock on the given semaphore handle and will assert otherwise.
140 */
141 virtual uint32_t writeLockLevel() const = 0;
142
143 virtual void lockWrite(LOCKVAL_SRC_POS_DECL) = 0;
144 virtual void unlockWrite() = 0;
145 virtual void lockRead(LOCKVAL_SRC_POS_DECL) = 0;
146 virtual void unlockRead() = 0;
147
148#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
149 virtual const char* describe() const = 0;
150#endif
151
152private:
153 // prohibit copy + assignment
154 LockHandle(const LockHandle&);
155 LockHandle& operator=(const LockHandle&);
156};
157
158/**
159 * Full-featured read/write semaphore handle implementation.
160 *
161 * This is an auxiliary base class for classes that need full-featured
162 * read/write locking as described in the AutoWriteLock class documentation.
163 * Instances of classes inherited from this class can be passed as arguments to
164 * the AutoWriteLock and AutoReadLock constructors.
165 */
166class RWLockHandle : public LockHandle
167{
168public:
169 RWLockHandle(VBoxLockingClass lockClass);
170 virtual ~RWLockHandle();
171
172 virtual bool isWriteLockOnCurrentThread() const;
173 virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const;
174
175 virtual void lockWrite(LOCKVAL_SRC_POS_DECL);
176 virtual void unlockWrite();
177 virtual void lockRead(LOCKVAL_SRC_POS_DECL);
178 virtual void unlockRead();
179
180 virtual uint32_t writeLockLevel() const;
181
182#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
183 virtual const char* describe() const;
184#endif
185
186private:
187 struct Data;
188 Data *m;
189
190 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(RWLockHandle); /* Shuts up MSC warning C4625. */
191};
192
193/**
194 * Write-only semaphore handle implementation.
195 *
196 * This is an auxiliary base class for classes that need write-only (exclusive)
197 * locking and do not need read (shared) locking. This implementation uses a
198 * cheap and fast critical section for both lockWrite() and lockRead() methods
199 * which makes a lockRead() call fully equivalent to the lockWrite() call and
200 * therefore makes it pointless to use instahces of this class with
201 * AutoReadLock instances -- shared locking will not be possible anyway and
202 * any call to lock() will block if there are lock owners on other threads.
203 *
204 * Use with care only when absolutely sure that shared locks are not necessary.
205 */
206class WriteLockHandle : public LockHandle
207{
208public:
209 WriteLockHandle(VBoxLockingClass lockClass);
210 virtual ~WriteLockHandle();
211 virtual bool isWriteLockOnCurrentThread() const;
212 virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const;
213
214 virtual void lockWrite(LOCKVAL_SRC_POS_DECL);
215 virtual void unlockWrite();
216 virtual void lockRead(LOCKVAL_SRC_POS_DECL);
217 virtual void unlockRead();
218 virtual uint32_t writeLockLevel() const;
219
220#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
221 virtual const char* describe() const;
222#endif
223
224private:
225 struct Data;
226 Data *m;
227
228 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WriteLockHandle); /* Shuts up MSC warning C4625. */
229};
230
231////////////////////////////////////////////////////////////////////////////////
232//
233// Lockable
234//
235////////////////////////////////////////////////////////////////////////////////
236
237/**
238 * Lockable interface.
239 *
240 * This is an abstract base for classes that need read/write locking. Unlike
241 * RWLockHandle and other classes that makes the read/write semaphore a part of
242 * class data, this class allows subclasses to decide which semaphore handle to
243 * use.
244 */
245class Lockable
246{
247public:
248
249 /**
250 * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock
251 * for locking. Subclasses are allowed to return @c NULL -- in this case,
252 * the AutoWriteLock/AutoReadLock object constructed using an instance of
253 * such subclass will simply turn into no-op.
254 */
255 virtual LockHandle *lockHandle() const = 0;
256
257 /**
258 * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>.
259 * Returns @c false if lockHandle() returns @c NULL.
260 */
261 bool isWriteLockOnCurrentThread()
262 {
263 LockHandle *h = lockHandle();
264 return h ? h->isWriteLockOnCurrentThread() : false;
265 }
266
267 /**
268 * Equivalent to <tt>#lockHandle()->isReadLockedOnCurrentThread()</tt>.
269 * Returns @c false if lockHandle() returns @c NULL.
270 * @note Use with care, simple debug assertions and similar only.
271 */
272 bool isReadLockedOnCurrentThread(bool fWannaHear = true)
273 {
274 LockHandle *h = lockHandle();
275 return h ? h->isReadLockedOnCurrentThread(fWannaHear) : false;
276 }
277};
278
279////////////////////////////////////////////////////////////////////////////////
280//
281// AutoLockBase
282//
283////////////////////////////////////////////////////////////////////////////////
284
285/**
286 * Abstract base class for all autolocks.
287 *
288 * This cannot be used directly. Use AutoReadLock or AutoWriteLock or AutoMultiWriteLock2/3
289 * which directly and indirectly derive from this.
290 *
291 * In the implementation, the instance data contains a list of lock handles.
292 * The class provides some utility functions to help locking and unlocking
293 * them.
294 */
295
296class AutoLockBase
297{
298protected:
299 AutoLockBase(uint32_t cHandles
300 COMMA_LOCKVAL_SRC_POS_DECL);
301 AutoLockBase(uint32_t cHandles,
302 LockHandle *pHandle
303 COMMA_LOCKVAL_SRC_POS_DECL);
304 virtual ~AutoLockBase();
305
306 struct Data;
307 Data *m;
308
309 virtual void callLockImpl(LockHandle &l) = 0;
310 virtual void callUnlockImpl(LockHandle &l) = 0;
311
312 void callLockOnAllHandles();
313 void callUnlockOnAllHandles();
314
315 void cleanup();
316
317public:
318 void acquire();
319 void release();
320
321private:
322 // prohibit copy + assignment
323 AutoLockBase(const AutoLockBase&);
324 AutoLockBase& operator=(const AutoLockBase&);
325};
326
327////////////////////////////////////////////////////////////////////////////////
328//
329// AutoReadLock
330//
331////////////////////////////////////////////////////////////////////////////////
332
333/**
334 * Automatic read lock. Use this with a RWLockHandle to request a read/write
335 * semaphore in read mode. You can also use this with a WriteLockHandle but
336 * that makes little sense since they treat read mode like write mode.
337 *
338 * If constructed with a RWLockHandle or an instance of Lockable (which in
339 * practice means any VirtualBoxBase derivative), it autoamtically requests
340 * the lock in read mode and releases the read lock in the destructor.
341 */
342class AutoReadLock : public AutoLockBase
343{
344public:
345
346 /**
347 * Constructs a null instance that does not manage any read/write
348 * semaphore.
349 *
350 * Note that all method calls on a null instance are no-ops. This allows to
351 * have the code where lock protection can be selected (or omitted) at
352 * runtime.
353 */
354 AutoReadLock(LOCKVAL_SRC_POS_DECL)
355 : AutoLockBase(1,
356 NULL
357 COMMA_LOCKVAL_SRC_POS_ARGS)
358 { }
359
360 /**
361 * Constructs a new instance that will start managing the given read/write
362 * semaphore by requesting a read lock.
363 */
364 AutoReadLock(LockHandle *aHandle
365 COMMA_LOCKVAL_SRC_POS_DECL)
366 : AutoLockBase(1,
367 aHandle
368 COMMA_LOCKVAL_SRC_POS_ARGS)
369 {
370 acquire();
371 }
372
373 /**
374 * Constructs a new instance that will start managing the given read/write
375 * semaphore by requesting a read lock.
376 */
377 AutoReadLock(LockHandle &aHandle
378 COMMA_LOCKVAL_SRC_POS_DECL)
379 : AutoLockBase(1,
380 &aHandle
381 COMMA_LOCKVAL_SRC_POS_ARGS)
382 {
383 acquire();
384 }
385
386 /**
387 * Constructs a new instance that will start managing the given read/write
388 * semaphore by requesting a read lock.
389 */
390 AutoReadLock(const Lockable &aLockable
391 COMMA_LOCKVAL_SRC_POS_DECL)
392 : AutoLockBase(1,
393 aLockable.lockHandle()
394 COMMA_LOCKVAL_SRC_POS_ARGS)
395 {
396 acquire();
397 }
398
399 /**
400 * Constructs a new instance that will start managing the given read/write
401 * semaphore by requesting a read lock.
402 */
403 AutoReadLock(const Lockable *aLockable
404 COMMA_LOCKVAL_SRC_POS_DECL)
405 : AutoLockBase(1,
406 aLockable ? aLockable->lockHandle() : NULL
407 COMMA_LOCKVAL_SRC_POS_ARGS)
408 {
409 acquire();
410 }
411
412 virtual ~AutoReadLock();
413
414 virtual void callLockImpl(LockHandle &l);
415 virtual void callUnlockImpl(LockHandle &l);
416
417private:
418 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReadLock); /* Shuts up MSC warning C4625. */
419};
420
421////////////////////////////////////////////////////////////////////////////////
422//
423// AutoWriteLockBase
424//
425////////////////////////////////////////////////////////////////////////////////
426
427/**
428 * Base class for all auto write locks.
429 *
430 * This cannot be used directly. Use AutoWriteLock or AutoMultiWriteLock2/3
431 * which derive from this.
432 *
433 * It has some utility methods for subclasses.
434 */
435class AutoWriteLockBase : public AutoLockBase
436{
437protected:
438 AutoWriteLockBase(uint32_t cHandles
439 COMMA_LOCKVAL_SRC_POS_DECL)
440 : AutoLockBase(cHandles
441 COMMA_LOCKVAL_SRC_POS_ARGS)
442 { }
443
444 AutoWriteLockBase(uint32_t cHandles,
445 LockHandle *pHandle
446 COMMA_LOCKVAL_SRC_POS_DECL)
447 : AutoLockBase(cHandles,
448 pHandle
449 COMMA_LOCKVAL_SRC_POS_ARGS)
450 { }
451
452 virtual ~AutoWriteLockBase()
453 { }
454
455 virtual void callLockImpl(LockHandle &l);
456 virtual void callUnlockImpl(LockHandle &l);
457
458private:
459 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLockBase); /* Shuts up MSC warning C4625. */
460};
461
462////////////////////////////////////////////////////////////////////////////////
463//
464// AutoWriteLock
465//
466////////////////////////////////////////////////////////////////////////////////
467
468/**
469 * Automatic write lock. Use this with a RWLockHandle to request a read/write
470 * semaphore in write mode. There can only ever be one writer of a read/write
471 * semaphore: while the lock is held in write mode, no other writer or reader
472 * can request the semaphore and will block.
473 *
474 * If constructed with a RWLockHandle or an instance of Lockable (which in
475 * practice means any VirtualBoxBase derivative), it autoamtically requests
476 * the lock in write mode and releases the write lock in the destructor.
477 *
478 * When used with a WriteLockHandle, it requests the semaphore contained therein
479 * exclusively.
480 */
481class AutoWriteLock : public AutoWriteLockBase
482{
483public:
484
485 /**
486 * Constructs a null instance that does not manage any read/write
487 * semaphore.
488 *
489 * Note that all method calls on a null instance are no-ops. This allows to
490 * have the code where lock protection can be selected (or omitted) at
491 * runtime.
492 */
493 AutoWriteLock(LOCKVAL_SRC_POS_DECL)
494 : AutoWriteLockBase(1,
495 NULL
496 COMMA_LOCKVAL_SRC_POS_ARGS)
497 { }
498
499 /**
500 * Constructs a new instance that will start managing the given read/write
501 * semaphore by requesting a write lock.
502 */
503 AutoWriteLock(LockHandle *aHandle
504 COMMA_LOCKVAL_SRC_POS_DECL)
505 : AutoWriteLockBase(1,
506 aHandle
507 COMMA_LOCKVAL_SRC_POS_ARGS)
508 {
509 acquire();
510 }
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(LockHandle &aHandle
517 COMMA_LOCKVAL_SRC_POS_DECL)
518 : AutoWriteLockBase(1,
519 &aHandle
520 COMMA_LOCKVAL_SRC_POS_ARGS)
521 {
522 acquire();
523 }
524
525 /**
526 * Constructs a new instance that will start managing the given read/write
527 * semaphore by requesting a write lock.
528 */
529 AutoWriteLock(const Lockable &aLockable
530 COMMA_LOCKVAL_SRC_POS_DECL)
531 : AutoWriteLockBase(1,
532 aLockable.lockHandle()
533 COMMA_LOCKVAL_SRC_POS_ARGS)
534 {
535 acquire();
536 }
537
538 /**
539 * Constructs a new instance that will start managing the given read/write
540 * semaphore by requesting a write lock.
541 */
542 AutoWriteLock(const Lockable *aLockable
543 COMMA_LOCKVAL_SRC_POS_DECL)
544 : AutoWriteLockBase(1,
545 aLockable ? aLockable->lockHandle() : NULL
546 COMMA_LOCKVAL_SRC_POS_ARGS)
547 {
548 acquire();
549 }
550
551 /**
552 * Constructs a new instance that will start managing the given read/write
553 * semaphore by requesting a write lock.
554 */
555 AutoWriteLock(uint32_t cHandles,
556 LockHandle** pHandles
557 COMMA_LOCKVAL_SRC_POS_DECL);
558
559 /**
560 * Release all write locks acquired by this instance through the #acquire()
561 * call and destroys the instance.
562 *
563 * Note that if there there are nested #acquire() calls without the
564 * corresponding number of #release() calls when the destructor is called, it
565 * will assert. This is because having an unbalanced number of nested locks
566 * is a program logic error which must be fixed.
567 */
568 virtual ~AutoWriteLock()
569 {
570 cleanup();
571 }
572
573 void attach(LockHandle *aHandle);
574
575 /** @see attach (LockHandle *) */
576 void attach(LockHandle &aHandle)
577 {
578 attach(&aHandle);
579 }
580
581 /** @see attach (LockHandle *) */
582 void attach(const Lockable &aLockable)
583 {
584 attach(aLockable.lockHandle());
585 }
586
587 /** @see attach (LockHandle *) */
588 void attach(const Lockable *aLockable)
589 {
590 attach(aLockable ? aLockable->lockHandle() : NULL);
591 }
592
593 bool isWriteLockOnCurrentThread() const;
594 uint32_t writeLockLevel() const;
595
596 bool isReadLockedOnCurrentThread(bool fWannaHear = true) const;
597
598private:
599 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLock); /* Shuts up MSC warning C4625. */
600};
601
602////////////////////////////////////////////////////////////////////////////////
603//
604// AutoMultiWriteLock*
605//
606////////////////////////////////////////////////////////////////////////////////
607
608/**
609 * A multi-write-lock containing two other write locks.
610 *
611 */
612class AutoMultiWriteLock2 : public AutoWriteLockBase
613{
614public:
615 AutoMultiWriteLock2(Lockable *pl1,
616 Lockable *pl2
617 COMMA_LOCKVAL_SRC_POS_DECL);
618 AutoMultiWriteLock2(LockHandle *pl1,
619 LockHandle *pl2
620 COMMA_LOCKVAL_SRC_POS_DECL);
621
622 virtual ~AutoMultiWriteLock2()
623 {
624 cleanup();
625 }
626
627private:
628 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock2); /* Shuts up MSC warning C4625. */
629};
630
631/**
632 * A multi-write-lock containing three other write locks.
633 *
634 */
635class AutoMultiWriteLock3 : public AutoWriteLockBase
636{
637public:
638 AutoMultiWriteLock3(Lockable *pl1,
639 Lockable *pl2,
640 Lockable *pl3
641 COMMA_LOCKVAL_SRC_POS_DECL);
642 AutoMultiWriteLock3(LockHandle *pl1,
643 LockHandle *pl2,
644 LockHandle *pl3
645 COMMA_LOCKVAL_SRC_POS_DECL);
646
647 virtual ~AutoMultiWriteLock3()
648 {
649 cleanup();
650 }
651
652private:
653 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock3); /* Shuts up MSC warning C4625. */
654};
655
656/**
657 * A multi-write-lock containing four other write locks.
658 *
659 */
660class AutoMultiWriteLock4 : public AutoWriteLockBase
661{
662public:
663 AutoMultiWriteLock4(Lockable *pl1,
664 Lockable *pl2,
665 Lockable *pl3,
666 Lockable *pl4
667 COMMA_LOCKVAL_SRC_POS_DECL);
668 AutoMultiWriteLock4(LockHandle *pl1,
669 LockHandle *pl2,
670 LockHandle *pl3,
671 LockHandle *pl4
672 COMMA_LOCKVAL_SRC_POS_DECL);
673
674 virtual ~AutoMultiWriteLock4()
675 {
676 cleanup();
677 }
678
679private:
680 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock4); /* Shuts up MSC warning C4625. */
681};
682
683} /* namespace util */
684
685/** @} */
686
687#endif
688
689/* 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