VirtualBox

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

Last change on this file since 58920 was 58110, checked in by vboxsync, 9 years ago

include,misc: Doxygen grouping adjustments, collecting all the VMM bits under one parent group, ditto for the COM library.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/** @file
2 * MS COM / XPCOM Abstraction Layer - Automatic locks, implementation.
3 */
4
5/*
6 * Copyright (C) 2006-2015 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 the current write lock level of this semaphore. The lock level
128 * determines the number of nested #lockWrite() calls on the given
129 * semaphore handle.
130 *
131 * Note that this call is valid only when the current thread owns a write
132 * lock on the given semaphore handle and will assert otherwise.
133 */
134 virtual uint32_t writeLockLevel() const = 0;
135
136 virtual void lockWrite(LOCKVAL_SRC_POS_DECL) = 0;
137 virtual void unlockWrite() = 0;
138 virtual void lockRead(LOCKVAL_SRC_POS_DECL) = 0;
139 virtual void unlockRead() = 0;
140
141#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
142 virtual const char* describe() const = 0;
143#endif
144
145private:
146 // prohibit copy + assignment
147 LockHandle(const LockHandle&);
148 LockHandle& operator=(const LockHandle&);
149};
150
151/**
152 * Full-featured read/write semaphore handle implementation.
153 *
154 * This is an auxiliary base class for classes that need full-featured
155 * read/write locking as described in the AutoWriteLock class documentation.
156 * Instances of classes inherited from this class can be passed as arguments to
157 * the AutoWriteLock and AutoReadLock constructors.
158 */
159class RWLockHandle : public LockHandle
160{
161public:
162 RWLockHandle(VBoxLockingClass lockClass);
163 virtual ~RWLockHandle();
164
165 virtual bool isWriteLockOnCurrentThread() const;
166
167 virtual void lockWrite(LOCKVAL_SRC_POS_DECL);
168 virtual void unlockWrite();
169 virtual void lockRead(LOCKVAL_SRC_POS_DECL);
170 virtual void unlockRead();
171
172 virtual uint32_t writeLockLevel() const;
173
174#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
175 virtual const char* describe() const;
176#endif
177
178private:
179 struct Data;
180 Data *m;
181};
182
183/**
184 * Write-only semaphore handle implementation.
185 *
186 * This is an auxiliary base class for classes that need write-only (exclusive)
187 * locking and do not need read (shared) locking. This implementation uses a
188 * cheap and fast critical section for both lockWrite() and lockRead() methods
189 * which makes a lockRead() call fully equivalent to the lockWrite() call and
190 * therefore makes it pointless to use instahces of this class with
191 * AutoReadLock instances -- shared locking will not be possible anyway and
192 * any call to lock() will block if there are lock owners on other threads.
193 *
194 * Use with care only when absolutely sure that shared locks are not necessary.
195 */
196class WriteLockHandle : public LockHandle
197{
198public:
199 WriteLockHandle(VBoxLockingClass lockClass);
200 virtual ~WriteLockHandle();
201 virtual bool isWriteLockOnCurrentThread() const;
202
203 virtual void lockWrite(LOCKVAL_SRC_POS_DECL);
204 virtual void unlockWrite();
205 virtual void lockRead(LOCKVAL_SRC_POS_DECL);
206 virtual void unlockRead();
207 virtual uint32_t writeLockLevel() const;
208
209#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
210 virtual const char* describe() const;
211#endif
212
213private:
214 struct Data;
215 Data *m;
216};
217
218////////////////////////////////////////////////////////////////////////////////
219//
220// Lockable
221//
222////////////////////////////////////////////////////////////////////////////////
223
224/**
225 * Lockable interface.
226 *
227 * This is an abstract base for classes that need read/write locking. Unlike
228 * RWLockHandle and other classes that makes the read/write semaphore a part of
229 * class data, this class allows subclasses to decide which semaphore handle to
230 * use.
231 */
232class Lockable
233{
234public:
235
236 /**
237 * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock
238 * for locking. Subclasses are allowed to return @c NULL -- in this case,
239 * the AutoWriteLock/AutoReadLock object constructed using an instance of
240 * such subclass will simply turn into no-op.
241 */
242 virtual LockHandle *lockHandle() const = 0;
243
244 /**
245 * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>.
246 * Returns @c false if lockHandle() returns @c NULL.
247 */
248 bool isWriteLockOnCurrentThread()
249 {
250 LockHandle *h = lockHandle();
251 return h ? h->isWriteLockOnCurrentThread() : false;
252 }
253};
254
255////////////////////////////////////////////////////////////////////////////////
256//
257// AutoLockBase
258//
259////////////////////////////////////////////////////////////////////////////////
260
261/**
262 * Abstract base class for all autolocks.
263 *
264 * This cannot be used directly. Use AutoReadLock or AutoWriteLock or AutoMultiWriteLock2/3
265 * which directly and indirectly derive from this.
266 *
267 * In the implementation, the instance data contains a list of lock handles.
268 * The class provides some utility functions to help locking and unlocking
269 * them.
270 */
271
272class AutoLockBase
273{
274protected:
275 AutoLockBase(uint32_t cHandles
276 COMMA_LOCKVAL_SRC_POS_DECL);
277 AutoLockBase(uint32_t cHandles,
278 LockHandle *pHandle
279 COMMA_LOCKVAL_SRC_POS_DECL);
280 virtual ~AutoLockBase();
281
282 struct Data;
283 Data *m;
284
285 virtual void callLockImpl(LockHandle &l) = 0;
286 virtual void callUnlockImpl(LockHandle &l) = 0;
287
288 void callLockOnAllHandles();
289 void callUnlockOnAllHandles();
290
291 void cleanup();
292
293public:
294 void acquire();
295 void release();
296
297private:
298 // prohibit copy + assignment
299 AutoLockBase(const AutoLockBase&);
300 AutoLockBase& operator=(const AutoLockBase&);
301};
302
303////////////////////////////////////////////////////////////////////////////////
304//
305// AutoReadLock
306//
307////////////////////////////////////////////////////////////////////////////////
308
309/**
310 * Automatic read lock. Use this with a RWLockHandle to request a read/write
311 * semaphore in read mode. You can also use this with a WriteLockHandle but
312 * that makes little sense since they treat read mode like write mode.
313 *
314 * If constructed with a RWLockHandle or an instance of Lockable (which in
315 * practice means any VirtualBoxBase derivative), it autoamtically requests
316 * the lock in read mode and releases the read lock in the destructor.
317 */
318class AutoReadLock : public AutoLockBase
319{
320public:
321
322 /**
323 * Constructs a null instance that does not manage any read/write
324 * semaphore.
325 *
326 * Note that all method calls on a null instance are no-ops. This allows to
327 * have the code where lock protection can be selected (or omitted) at
328 * runtime.
329 */
330 AutoReadLock(LOCKVAL_SRC_POS_DECL)
331 : AutoLockBase(1,
332 NULL
333 COMMA_LOCKVAL_SRC_POS_ARGS)
334 { }
335
336 /**
337 * Constructs a new instance that will start managing the given read/write
338 * semaphore by requesting a read lock.
339 */
340 AutoReadLock(LockHandle *aHandle
341 COMMA_LOCKVAL_SRC_POS_DECL)
342 : AutoLockBase(1,
343 aHandle
344 COMMA_LOCKVAL_SRC_POS_ARGS)
345 {
346 acquire();
347 }
348
349 /**
350 * Constructs a new instance that will start managing the given read/write
351 * semaphore by requesting a read lock.
352 */
353 AutoReadLock(LockHandle &aHandle
354 COMMA_LOCKVAL_SRC_POS_DECL)
355 : AutoLockBase(1,
356 &aHandle
357 COMMA_LOCKVAL_SRC_POS_ARGS)
358 {
359 acquire();
360 }
361
362 /**
363 * Constructs a new instance that will start managing the given read/write
364 * semaphore by requesting a read lock.
365 */
366 AutoReadLock(const Lockable &aLockable
367 COMMA_LOCKVAL_SRC_POS_DECL)
368 : AutoLockBase(1,
369 aLockable.lockHandle()
370 COMMA_LOCKVAL_SRC_POS_ARGS)
371 {
372 acquire();
373 }
374
375 /**
376 * Constructs a new instance that will start managing the given read/write
377 * semaphore by requesting a read lock.
378 */
379 AutoReadLock(const Lockable *aLockable
380 COMMA_LOCKVAL_SRC_POS_DECL)
381 : AutoLockBase(1,
382 aLockable ? aLockable->lockHandle() : NULL
383 COMMA_LOCKVAL_SRC_POS_ARGS)
384 {
385 acquire();
386 }
387
388 virtual ~AutoReadLock();
389
390 virtual void callLockImpl(LockHandle &l);
391 virtual void callUnlockImpl(LockHandle &l);
392};
393
394////////////////////////////////////////////////////////////////////////////////
395//
396// AutoWriteLockBase
397//
398////////////////////////////////////////////////////////////////////////////////
399
400/**
401 * Base class for all auto write locks.
402 *
403 * This cannot be used directly. Use AutoWriteLock or AutoMultiWriteLock2/3
404 * which derive from this.
405 *
406 * It has some utility methods for subclasses.
407 */
408class AutoWriteLockBase : public AutoLockBase
409{
410protected:
411 AutoWriteLockBase(uint32_t cHandles
412 COMMA_LOCKVAL_SRC_POS_DECL)
413 : AutoLockBase(cHandles
414 COMMA_LOCKVAL_SRC_POS_ARGS)
415 { }
416
417 AutoWriteLockBase(uint32_t cHandles,
418 LockHandle *pHandle
419 COMMA_LOCKVAL_SRC_POS_DECL)
420 : AutoLockBase(cHandles,
421 pHandle
422 COMMA_LOCKVAL_SRC_POS_ARGS)
423 { }
424
425 virtual ~AutoWriteLockBase()
426 { }
427
428 virtual void callLockImpl(LockHandle &l);
429 virtual void callUnlockImpl(LockHandle &l);
430};
431
432////////////////////////////////////////////////////////////////////////////////
433//
434// AutoWriteLock
435//
436////////////////////////////////////////////////////////////////////////////////
437
438/**
439 * Automatic write lock. Use this with a RWLockHandle to request a read/write
440 * semaphore in write mode. There can only ever be one writer of a read/write
441 * semaphore: while the lock is held in write mode, no other writer or reader
442 * can request the semaphore and will block.
443 *
444 * If constructed with a RWLockHandle or an instance of Lockable (which in
445 * practice means any VirtualBoxBase derivative), it autoamtically requests
446 * the lock in write mode and releases the write lock in the destructor.
447 *
448 * When used with a WriteLockHandle, it requests the semaphore contained therein
449 * exclusively.
450 */
451class AutoWriteLock : public AutoWriteLockBase
452{
453public:
454
455 /**
456 * Constructs a null instance that does not manage any read/write
457 * semaphore.
458 *
459 * Note that all method calls on a null instance are no-ops. This allows to
460 * have the code where lock protection can be selected (or omitted) at
461 * runtime.
462 */
463 AutoWriteLock(LOCKVAL_SRC_POS_DECL)
464 : AutoWriteLockBase(1,
465 NULL
466 COMMA_LOCKVAL_SRC_POS_ARGS)
467 { }
468
469 /**
470 * Constructs a new instance that will start managing the given read/write
471 * semaphore by requesting a write lock.
472 */
473 AutoWriteLock(LockHandle *aHandle
474 COMMA_LOCKVAL_SRC_POS_DECL)
475 : AutoWriteLockBase(1,
476 aHandle
477 COMMA_LOCKVAL_SRC_POS_ARGS)
478 {
479 acquire();
480 }
481
482 /**
483 * Constructs a new instance that will start managing the given read/write
484 * semaphore by requesting a write lock.
485 */
486 AutoWriteLock(LockHandle &aHandle
487 COMMA_LOCKVAL_SRC_POS_DECL)
488 : AutoWriteLockBase(1,
489 &aHandle
490 COMMA_LOCKVAL_SRC_POS_ARGS)
491 {
492 acquire();
493 }
494
495 /**
496 * Constructs a new instance that will start managing the given read/write
497 * semaphore by requesting a write lock.
498 */
499 AutoWriteLock(const Lockable &aLockable
500 COMMA_LOCKVAL_SRC_POS_DECL)
501 : AutoWriteLockBase(1,
502 aLockable.lockHandle()
503 COMMA_LOCKVAL_SRC_POS_ARGS)
504 {
505 acquire();
506 }
507
508 /**
509 * Constructs a new instance that will start managing the given read/write
510 * semaphore by requesting a write lock.
511 */
512 AutoWriteLock(const Lockable *aLockable
513 COMMA_LOCKVAL_SRC_POS_DECL)
514 : AutoWriteLockBase(1,
515 aLockable ? aLockable->lockHandle() : NULL
516 COMMA_LOCKVAL_SRC_POS_ARGS)
517 {
518 acquire();
519 }
520
521 /**
522 * Constructs a new instance that will start managing the given read/write
523 * semaphore by requesting a write lock.
524 */
525 AutoWriteLock(uint32_t cHandles,
526 LockHandle** pHandles
527 COMMA_LOCKVAL_SRC_POS_DECL);
528
529 /**
530 * Release all write locks acquired by this instance through the #acquire()
531 * call and destroys the instance.
532 *
533 * Note that if there there are nested #acquire() calls without the
534 * corresponding number of #release() calls when the destructor is called, it
535 * will assert. This is because having an unbalanced number of nested locks
536 * is a program logic error which must be fixed.
537 */
538 virtual ~AutoWriteLock()
539 {
540 cleanup();
541 }
542
543 void attach(LockHandle *aHandle);
544
545 /** @see attach (LockHandle *) */
546 void attach(LockHandle &aHandle)
547 {
548 attach(&aHandle);
549 }
550
551 /** @see attach (LockHandle *) */
552 void attach(const Lockable &aLockable)
553 {
554 attach(aLockable.lockHandle());
555 }
556
557 /** @see attach (LockHandle *) */
558 void attach(const Lockable *aLockable)
559 {
560 attach(aLockable ? aLockable->lockHandle() : NULL);
561 }
562
563 bool isWriteLockOnCurrentThread() const;
564 uint32_t writeLockLevel() const;
565};
566
567////////////////////////////////////////////////////////////////////////////////
568//
569// AutoMultiWriteLock*
570//
571////////////////////////////////////////////////////////////////////////////////
572
573/**
574 * A multi-write-lock containing two other write locks.
575 *
576 */
577class AutoMultiWriteLock2 : public AutoWriteLockBase
578{
579public:
580 AutoMultiWriteLock2(Lockable *pl1,
581 Lockable *pl2
582 COMMA_LOCKVAL_SRC_POS_DECL);
583 AutoMultiWriteLock2(LockHandle *pl1,
584 LockHandle *pl2
585 COMMA_LOCKVAL_SRC_POS_DECL);
586
587 virtual ~AutoMultiWriteLock2()
588 {
589 cleanup();
590 }
591};
592
593/**
594 * A multi-write-lock containing three other write locks.
595 *
596 */
597class AutoMultiWriteLock3 : public AutoWriteLockBase
598{
599public:
600 AutoMultiWriteLock3(Lockable *pl1,
601 Lockable *pl2,
602 Lockable *pl3
603 COMMA_LOCKVAL_SRC_POS_DECL);
604 AutoMultiWriteLock3(LockHandle *pl1,
605 LockHandle *pl2,
606 LockHandle *pl3
607 COMMA_LOCKVAL_SRC_POS_DECL);
608
609 virtual ~AutoMultiWriteLock3()
610 {
611 cleanup();
612 }
613};
614
615/**
616 * A multi-write-lock containing four other write locks.
617 *
618 */
619class AutoMultiWriteLock4 : public AutoWriteLockBase
620{
621public:
622 AutoMultiWriteLock4(Lockable *pl1,
623 Lockable *pl2,
624 Lockable *pl3,
625 Lockable *pl4
626 COMMA_LOCKVAL_SRC_POS_DECL);
627 AutoMultiWriteLock4(LockHandle *pl1,
628 LockHandle *pl2,
629 LockHandle *pl3,
630 LockHandle *pl4
631 COMMA_LOCKVAL_SRC_POS_DECL);
632
633 virtual ~AutoMultiWriteLock4()
634 {
635 cleanup();
636 }
637};
638
639} /* namespace util */
640
641/** @} */
642
643#endif
644
645/* 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