VirtualBox

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

Last change on this file since 76558 was 76558, checked in by vboxsync, 6 years ago

include/VBox: Use VBOX_INCLUDED_ rather than _vbox_ as header guard prefix, letting scm enforce this (thereby avoiding copy&paste errors like NativeEventQueue.h).

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