VirtualBox

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

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

Main: lock validation adjustments

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