VirtualBox

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

Last change on this file since 25414 was 25408, checked in by vboxsync, 14 years ago

Main/Locking: fix regression in AutoMultiWriteLock that caused confusion in how many times locks needed to be unlocked; this caused lock validation to fail in restoreSnapshot(); also don't require unlocking to happen in exactly the reverse order of locking; documentation

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

© 2023 Oracle
ContactPrivacy policyTerms of Use