VirtualBox

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

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

Main: more locking order validation fixes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette