VirtualBox

source: vbox/trunk/src/VBox/Main/include/AutoLock.h@ 25809

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

Main: adjust lock validation code to use IPRT lock validation, first steps (not active yet)

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