VirtualBox

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

Last change on this file since 90439 was 84342, checked in by vboxsync, 5 years ago

Main: VC++ 19.2 adjustments. bugref:8489

  • 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-2020 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 virtual ~Lockable() { } /* To make VC++ 2019 happy. */
252
253 /**
254 * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock
255 * for locking. Subclasses are allowed to return @c NULL -- in this case,
256 * the AutoWriteLock/AutoReadLock object constructed using an instance of
257 * such subclass will simply turn into no-op.
258 */
259 virtual LockHandle *lockHandle() const = 0;
260
261 /**
262 * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>.
263 * Returns @c false if lockHandle() returns @c NULL.
264 */
265 bool isWriteLockOnCurrentThread()
266 {
267 LockHandle *h = lockHandle();
268 return h ? h->isWriteLockOnCurrentThread() : false;
269 }
270
271 /**
272 * Equivalent to <tt>#lockHandle()->isReadLockedOnCurrentThread()</tt>.
273 * Returns @c false if lockHandle() returns @c NULL.
274 * @note Use with care, simple debug assertions and similar only.
275 */
276 bool isReadLockedOnCurrentThread(bool fWannaHear = true) const
277 {
278 LockHandle *h = lockHandle();
279 return h ? h->isReadLockedOnCurrentThread(fWannaHear) : false;
280 }
281};
282
283////////////////////////////////////////////////////////////////////////////////
284//
285// AutoLockBase
286//
287////////////////////////////////////////////////////////////////////////////////
288
289/**
290 * Abstract base class for all autolocks.
291 *
292 * This cannot be used directly. Use AutoReadLock or AutoWriteLock or AutoMultiWriteLock2/3
293 * which directly and indirectly derive from this.
294 *
295 * In the implementation, the instance data contains a list of lock handles.
296 * The class provides some utility functions to help locking and unlocking
297 * them.
298 */
299
300class AutoLockBase
301{
302protected:
303 AutoLockBase(uint32_t cHandles
304 COMMA_LOCKVAL_SRC_POS_DECL);
305 AutoLockBase(uint32_t cHandles,
306 LockHandle *pHandle
307 COMMA_LOCKVAL_SRC_POS_DECL);
308 virtual ~AutoLockBase();
309
310 struct Data;
311 Data *m;
312
313 virtual void callLockImpl(LockHandle &l) = 0;
314 virtual void callUnlockImpl(LockHandle &l) = 0;
315
316 void callLockOnAllHandles();
317 void callUnlockOnAllHandles();
318
319 void cleanup();
320
321public:
322 void acquire();
323 void release();
324
325private:
326 // prohibit copy + assignment
327 AutoLockBase(const AutoLockBase&);
328 AutoLockBase& operator=(const AutoLockBase&);
329};
330
331////////////////////////////////////////////////////////////////////////////////
332//
333// AutoReadLock
334//
335////////////////////////////////////////////////////////////////////////////////
336
337/**
338 * Automatic read lock. Use this with a RWLockHandle to request a read/write
339 * semaphore in read mode. You can also use this with a WriteLockHandle but
340 * that makes little sense since they treat read mode like write mode.
341 *
342 * If constructed with a RWLockHandle or an instance of Lockable (which in
343 * practice means any VirtualBoxBase derivative), it autoamtically requests
344 * the lock in read mode and releases the read lock in the destructor.
345 */
346class AutoReadLock : public AutoLockBase
347{
348public:
349
350 /**
351 * Constructs a null instance that does not manage any read/write
352 * semaphore.
353 *
354 * Note that all method calls on a null instance are no-ops. This allows to
355 * have the code where lock protection can be selected (or omitted) at
356 * runtime.
357 */
358 AutoReadLock(LOCKVAL_SRC_POS_DECL)
359 : AutoLockBase(1,
360 NULL
361 COMMA_LOCKVAL_SRC_POS_ARGS)
362 { }
363
364 /**
365 * Constructs a new instance that will start managing the given read/write
366 * semaphore by requesting a read lock.
367 */
368 AutoReadLock(LockHandle *aHandle
369 COMMA_LOCKVAL_SRC_POS_DECL)
370 : AutoLockBase(1,
371 aHandle
372 COMMA_LOCKVAL_SRC_POS_ARGS)
373 {
374 acquire();
375 }
376
377 /**
378 * Constructs a new instance that will start managing the given read/write
379 * semaphore by requesting a read lock.
380 */
381 AutoReadLock(LockHandle &aHandle
382 COMMA_LOCKVAL_SRC_POS_DECL)
383 : AutoLockBase(1,
384 &aHandle
385 COMMA_LOCKVAL_SRC_POS_ARGS)
386 {
387 acquire();
388 }
389
390 /**
391 * Constructs a new instance that will start managing the given read/write
392 * semaphore by requesting a read lock.
393 */
394 AutoReadLock(const Lockable &aLockable
395 COMMA_LOCKVAL_SRC_POS_DECL)
396 : AutoLockBase(1,
397 aLockable.lockHandle()
398 COMMA_LOCKVAL_SRC_POS_ARGS)
399 {
400 acquire();
401 }
402
403 /**
404 * Constructs a new instance that will start managing the given read/write
405 * semaphore by requesting a read lock.
406 */
407 AutoReadLock(const Lockable *aLockable
408 COMMA_LOCKVAL_SRC_POS_DECL)
409 : AutoLockBase(1,
410 aLockable ? aLockable->lockHandle() : NULL
411 COMMA_LOCKVAL_SRC_POS_ARGS)
412 {
413 acquire();
414 }
415
416 virtual ~AutoReadLock();
417
418 virtual void callLockImpl(LockHandle &l);
419 virtual void callUnlockImpl(LockHandle &l);
420
421private:
422 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReadLock); /* Shuts up MSC warning C4625. */
423};
424
425////////////////////////////////////////////////////////////////////////////////
426//
427// AutoWriteLockBase
428//
429////////////////////////////////////////////////////////////////////////////////
430
431/**
432 * Base class for all auto write locks.
433 *
434 * This cannot be used directly. Use AutoWriteLock or AutoMultiWriteLock2/3
435 * which derive from this.
436 *
437 * It has some utility methods for subclasses.
438 */
439class AutoWriteLockBase : public AutoLockBase
440{
441protected:
442 AutoWriteLockBase(uint32_t cHandles
443 COMMA_LOCKVAL_SRC_POS_DECL)
444 : AutoLockBase(cHandles
445 COMMA_LOCKVAL_SRC_POS_ARGS)
446 { }
447
448 AutoWriteLockBase(uint32_t cHandles,
449 LockHandle *pHandle
450 COMMA_LOCKVAL_SRC_POS_DECL)
451 : AutoLockBase(cHandles,
452 pHandle
453 COMMA_LOCKVAL_SRC_POS_ARGS)
454 { }
455
456 virtual ~AutoWriteLockBase()
457 { }
458
459 virtual void callLockImpl(LockHandle &l);
460 virtual void callUnlockImpl(LockHandle &l);
461
462private:
463 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLockBase); /* Shuts up MSC warning C4625. */
464};
465
466////////////////////////////////////////////////////////////////////////////////
467//
468// AutoWriteLock
469//
470////////////////////////////////////////////////////////////////////////////////
471
472/**
473 * Automatic write lock. Use this with a RWLockHandle to request a read/write
474 * semaphore in write mode. There can only ever be one writer of a read/write
475 * semaphore: while the lock is held in write mode, no other writer or reader
476 * can request the semaphore and will block.
477 *
478 * If constructed with a RWLockHandle or an instance of Lockable (which in
479 * practice means any VirtualBoxBase derivative), it autoamtically requests
480 * the lock in write mode and releases the write lock in the destructor.
481 *
482 * When used with a WriteLockHandle, it requests the semaphore contained therein
483 * exclusively.
484 */
485class AutoWriteLock : public AutoWriteLockBase
486{
487public:
488
489 /**
490 * Constructs a null instance that does not manage any read/write
491 * semaphore.
492 *
493 * Note that all method calls on a null instance are no-ops. This allows to
494 * have the code where lock protection can be selected (or omitted) at
495 * runtime.
496 */
497 AutoWriteLock(LOCKVAL_SRC_POS_DECL)
498 : AutoWriteLockBase(1,
499 NULL
500 COMMA_LOCKVAL_SRC_POS_ARGS)
501 { }
502
503 /**
504 * Constructs a new instance that will start managing the given read/write
505 * semaphore by requesting a write lock.
506 */
507 AutoWriteLock(LockHandle *aHandle
508 COMMA_LOCKVAL_SRC_POS_DECL)
509 : AutoWriteLockBase(1,
510 aHandle
511 COMMA_LOCKVAL_SRC_POS_ARGS)
512 {
513 acquire();
514 }
515
516 /**
517 * Constructs a new instance that will start managing the given read/write
518 * semaphore by requesting a write lock.
519 */
520 AutoWriteLock(LockHandle &aHandle
521 COMMA_LOCKVAL_SRC_POS_DECL)
522 : AutoWriteLockBase(1,
523 &aHandle
524 COMMA_LOCKVAL_SRC_POS_ARGS)
525 {
526 acquire();
527 }
528
529 /**
530 * Constructs a new instance that will start managing the given read/write
531 * semaphore by requesting a write lock.
532 */
533 AutoWriteLock(const Lockable &aLockable
534 COMMA_LOCKVAL_SRC_POS_DECL)
535 : AutoWriteLockBase(1,
536 aLockable.lockHandle()
537 COMMA_LOCKVAL_SRC_POS_ARGS)
538 {
539 acquire();
540 }
541
542 /**
543 * Constructs a new instance that will start managing the given read/write
544 * semaphore by requesting a write lock.
545 */
546 AutoWriteLock(const Lockable *aLockable
547 COMMA_LOCKVAL_SRC_POS_DECL)
548 : AutoWriteLockBase(1,
549 aLockable ? aLockable->lockHandle() : NULL
550 COMMA_LOCKVAL_SRC_POS_ARGS)
551 {
552 acquire();
553 }
554
555 /**
556 * Constructs a new instance that will start managing the given read/write
557 * semaphore by requesting a write lock.
558 */
559 AutoWriteLock(uint32_t cHandles,
560 LockHandle** pHandles
561 COMMA_LOCKVAL_SRC_POS_DECL);
562
563 /**
564 * Release all write locks acquired by this instance through the #acquire()
565 * call and destroys the instance.
566 *
567 * Note that if there there are nested #acquire() calls without the
568 * corresponding number of #release() calls when the destructor is called, it
569 * will assert. This is because having an unbalanced number of nested locks
570 * is a program logic error which must be fixed.
571 */
572 virtual ~AutoWriteLock()
573 {
574 cleanup();
575 }
576
577 void attach(LockHandle *aHandle);
578
579 /** @see attach (LockHandle *) */
580 void attach(LockHandle &aHandle)
581 {
582 attach(&aHandle);
583 }
584
585 /** @see attach (LockHandle *) */
586 void attach(const Lockable &aLockable)
587 {
588 attach(aLockable.lockHandle());
589 }
590
591 /** @see attach (LockHandle *) */
592 void attach(const Lockable *aLockable)
593 {
594 attach(aLockable ? aLockable->lockHandle() : NULL);
595 }
596
597 bool isWriteLockOnCurrentThread() const;
598 uint32_t writeLockLevel() const;
599
600 bool isReadLockedOnCurrentThread(bool fWannaHear = true) const;
601
602private:
603 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLock); /* Shuts up MSC warning C4625. */
604};
605
606////////////////////////////////////////////////////////////////////////////////
607//
608// AutoMultiWriteLock*
609//
610////////////////////////////////////////////////////////////////////////////////
611
612/**
613 * A multi-write-lock containing two other write locks.
614 *
615 */
616class AutoMultiWriteLock2 : public AutoWriteLockBase
617{
618public:
619 AutoMultiWriteLock2(Lockable *pl1,
620 Lockable *pl2
621 COMMA_LOCKVAL_SRC_POS_DECL);
622 AutoMultiWriteLock2(LockHandle *pl1,
623 LockHandle *pl2
624 COMMA_LOCKVAL_SRC_POS_DECL);
625
626 virtual ~AutoMultiWriteLock2()
627 {
628 cleanup();
629 }
630
631private:
632 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock2); /* Shuts up MSC warning C4625. */
633};
634
635/**
636 * A multi-write-lock containing three other write locks.
637 *
638 */
639class AutoMultiWriteLock3 : public AutoWriteLockBase
640{
641public:
642 AutoMultiWriteLock3(Lockable *pl1,
643 Lockable *pl2,
644 Lockable *pl3
645 COMMA_LOCKVAL_SRC_POS_DECL);
646 AutoMultiWriteLock3(LockHandle *pl1,
647 LockHandle *pl2,
648 LockHandle *pl3
649 COMMA_LOCKVAL_SRC_POS_DECL);
650
651 virtual ~AutoMultiWriteLock3()
652 {
653 cleanup();
654 }
655
656private:
657 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock3); /* Shuts up MSC warning C4625. */
658};
659
660/**
661 * A multi-write-lock containing four other write locks.
662 *
663 */
664class AutoMultiWriteLock4 : public AutoWriteLockBase
665{
666public:
667 AutoMultiWriteLock4(Lockable *pl1,
668 Lockable *pl2,
669 Lockable *pl3,
670 Lockable *pl4
671 COMMA_LOCKVAL_SRC_POS_DECL);
672 AutoMultiWriteLock4(LockHandle *pl1,
673 LockHandle *pl2,
674 LockHandle *pl3,
675 LockHandle *pl4
676 COMMA_LOCKVAL_SRC_POS_DECL);
677
678 virtual ~AutoMultiWriteLock4()
679 {
680 cleanup();
681 }
682
683private:
684 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock4); /* Shuts up MSC warning C4625. */
685};
686
687} /* namespace util */
688
689/** @} */
690
691#endif /* !VBOX_INCLUDED_com_AutoLock_h */
692
693/* 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