VirtualBox

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

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

Main: preparation for deadlock detection: make lock instance data private, no more global semaphore IPRT includes

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