VirtualBox

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

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

Main: lock validator, first batch: implement per-thread stack to trace locking (disabled by default, use VBOX_WITH_LOCK_VALIDATOR, but that WILL FAIL presently)

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