VirtualBox

source: vbox/trunk/src/VBox/Main/AutoLock.cpp@ 25287

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

Main: preparation for deadlock detection: flatten LockHandle hierarchy, some documentation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 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#include "AutoLock.h"
23
24#include "Logging.h"
25
26#include <iprt/string.h>
27
28#include <vector>
29
30namespace util
31{
32
33////////////////////////////////////////////////////////////////////////////////
34//
35// RWLockHandle
36//
37////////////////////////////////////////////////////////////////////////////////
38
39RWLockHandle::RWLockHandle()
40{
41 int vrc = RTSemRWCreate (&mSemRW);
42 AssertRC (vrc);
43}
44
45
46RWLockHandle::~RWLockHandle()
47{
48 RTSemRWDestroy (mSemRW);
49}
50
51
52bool RWLockHandle::isWriteLockOnCurrentThread() const
53{
54 return RTSemRWIsWriteOwner (mSemRW);
55}
56
57
58void RWLockHandle::lockWrite()
59{
60 int vrc = RTSemRWRequestWrite (mSemRW, RT_INDEFINITE_WAIT);
61 AssertRC (vrc);
62}
63
64
65void RWLockHandle::unlockWrite()
66{
67 int vrc = RTSemRWReleaseWrite (mSemRW);
68 AssertRC (vrc);
69}
70
71
72void RWLockHandle::lockRead()
73{
74 int vrc = RTSemRWRequestRead (mSemRW, RT_INDEFINITE_WAIT);
75 AssertRC (vrc);
76}
77
78
79void RWLockHandle::unlockRead()
80{
81 int vrc = RTSemRWReleaseRead (mSemRW);
82 AssertRC (vrc);
83}
84
85
86uint32_t RWLockHandle::writeLockLevel() const
87{
88 return RTSemRWGetWriteRecursion (mSemRW);
89}
90
91////////////////////////////////////////////////////////////////////////////////
92//
93// WriteLockHandle
94//
95////////////////////////////////////////////////////////////////////////////////
96
97WriteLockHandle::WriteLockHandle()
98{
99 RTCritSectInit(&mCritSect);
100}
101
102/*virtual*/ WriteLockHandle::~WriteLockHandle()
103{
104 RTCritSectDelete(&mCritSect);
105}
106
107/*virtual*/ bool WriteLockHandle::isWriteLockOnCurrentThread() const
108{
109 return RTCritSectIsOwner(&mCritSect);
110}
111
112/*virtual*/ void WriteLockHandle::lockWrite()
113{
114#if defined(DEBUG)
115 RTCritSectEnterDebug(&mCritSect,
116 "WriteLockHandle::lockWrite() return address >>>",
117 0, (RTUINTPTR) ASMReturnAddress());
118#else
119 RTCritSectEnter(&mCritSect);
120#endif
121}
122
123/*virtual*/ void WriteLockHandle::unlockWrite()
124{
125 RTCritSectLeave(&mCritSect);
126}
127
128/*virtual*/ void WriteLockHandle::lockRead()
129{
130 lockWrite();
131}
132
133/*virtual*/ void WriteLockHandle::unlockRead()
134{
135 unlockWrite();
136}
137
138/*virtual*/ uint32_t WriteLockHandle::writeLockLevel() const
139{
140 return RTCritSectGetRecursion(&mCritSect);
141}
142
143////////////////////////////////////////////////////////////////////////////////
144//
145// AutoLockBase
146//
147////////////////////////////////////////////////////////////////////////////////
148
149typedef std::vector<LockHandle*> HandlesVector;
150typedef std::vector<uint32_t> CountsVector;
151
152struct AutoLockBase::Data
153{
154 Data(size_t cHandles)
155 : fIsLocked(false),
156 aHandles(cHandles), // size of array
157 acUnlockedInLeave(cHandles)
158 {
159 for (uint32_t i = 0; i < cHandles; ++i)
160 {
161 acUnlockedInLeave[i] = 0;
162 aHandles[i] = NULL;
163 }
164 }
165
166 bool fIsLocked; // if true, then all items in aHandles are locked by this AutoLock and
167 // need to be unlocked in the destructor
168 HandlesVector aHandles; // array (vector) of LockHandle instances; in the case of AutoWriteLock
169 // and AutoReadLock, there will only be one item on the list; with the
170 // AutoMulti* derivatives, there will be multiple
171 CountsVector acUnlockedInLeave; // for each lock handle, how many times the handle was unlocked in leave(); otherwise 0
172};
173
174AutoLockBase::AutoLockBase(uint32_t cHandles)
175{
176 m = new Data(cHandles);
177}
178
179AutoLockBase::AutoLockBase(uint32_t cHandles, LockHandle *pHandle)
180{
181 Assert(cHandles == 1);
182 m = new Data(1);
183 m->aHandles[0] = pHandle;
184}
185
186AutoLockBase::~AutoLockBase()
187{
188 delete m;
189}
190
191/**
192 * Requests ownership of all contained lock handles by calling
193 * the pure virtual callLockImpl() function on each of them,
194 * which must be implemented by the descendant class; in the
195 * implementation, AutoWriteLock will request a write lock
196 * whereas AutoReadLock will request a read lock.
197 *
198 * Does *not* modify the lock counts in the member variables.
199 */
200void AutoLockBase::callLockOnAllHandles()
201{
202 for (HandlesVector::iterator it = m->aHandles.begin();
203 it != m->aHandles.end();
204 ++it)
205 {
206 LockHandle *pHandle = *it;
207 if (pHandle)
208 // call virtual function implemented in AutoWriteLock or AutoReadLock
209 this->callLockImpl(*pHandle);
210 }
211}
212
213/**
214 * Releases ownership of all contained lock handles by calling
215 * the pure virtual callUnlockImpl() function on each of them,
216 * which must be implemented by the descendant class; in the
217 * implementation, AutoWriteLock will release a write lock
218 * whereas AutoReadLock will release a read lock.
219 *
220 * Does *not* modify the lock counts in the member variables.
221 */
222void AutoLockBase::callUnlockOnAllHandles()
223{
224 for (HandlesVector::iterator it = m->aHandles.begin();
225 it != m->aHandles.end();
226 ++it)
227 {
228 LockHandle *pHandle = *it;
229 if (pHandle)
230 // call virtual function implemented in AutoWriteLock or AutoReadLock
231 this->callUnlockImpl(*pHandle);
232 }
233}
234
235/**
236 * Destructor implementation that can also be called explicitly, if required.
237 * Restores the exact state before the AutoLock was created; that is, unlocks
238 * all contained semaphores and might actually lock them again if leave()
239 * was called during the AutoLock's lifetime.
240 */
241void AutoLockBase::cleanup()
242{
243 bool fAnyUnlockedInLeave = false;
244
245 uint32_t i = 0;
246 for (HandlesVector::iterator it = m->aHandles.begin();
247 it != m->aHandles.end();
248 ++it)
249 {
250 LockHandle *pHandle = *it;
251 if (pHandle)
252 {
253 if (m->acUnlockedInLeave[i])
254 {
255 // there was a leave() before the destruction: then restore the
256 // lock level that might have been set by locks other than our own
257 if (m->fIsLocked)
258 {
259 --m->acUnlockedInLeave[i];
260 fAnyUnlockedInLeave = true;
261 }
262 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
263 callLockImpl(*pHandle);
264 }
265 }
266 ++i;
267 }
268
269 if (m->fIsLocked && !fAnyUnlockedInLeave)
270 callUnlockOnAllHandles();
271}
272
273/**
274 * Requests ownership of all contained semaphores. Public method that can
275 * only be called once and that also gets called by the AutoLock constructors.
276 */
277void AutoLockBase::acquire()
278{
279 AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
280 callLockOnAllHandles();
281 m->fIsLocked = true;
282}
283
284/**
285 * Releases ownership of all contained semaphores. Public method.
286 */
287void AutoLockBase::release()
288{
289 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
290 callUnlockOnAllHandles();
291 m->fIsLocked = false;
292}
293
294////////////////////////////////////////////////////////////////////////////////
295//
296// AutoReadLock
297//
298////////////////////////////////////////////////////////////////////////////////
299
300/**
301 * Release all read locks acquired by this instance through the #lock()
302 * call and destroys the instance.
303 *
304 * Note that if there there are nested #lock() calls without the
305 * corresponding number of #unlock() calls when the destructor is called, it
306 * will assert. This is because having an unbalanced number of nested locks
307 * is a program logic error which must be fixed.
308 */
309/*virtual*/ AutoReadLock::~AutoReadLock()
310{
311 LockHandle *pHandle = m->aHandles[0];
312
313 if (pHandle)
314 {
315 if (m->fIsLocked)
316 pHandle->unlockRead();
317 }
318}
319
320/**
321 * Implementation of the pure virtual declared in AutoLockBase.
322 * This gets called by AutoLockBase.acquire() to actually request
323 * the semaphore; in the AutoReadLock implementation, we request
324 * the semaphore in read mode.
325 */
326/*virtual*/ void AutoReadLock::callLockImpl(LockHandle &l)
327{
328 l.lockRead();
329}
330
331/**
332 * Implementation of the pure virtual declared in AutoLockBase.
333 * This gets called by AutoLockBase.release() to actually release
334 * the semaphore; in the AutoReadLock implementation, we release
335 * the semaphore in read mode.
336 */
337/*virtual*/ void AutoReadLock::callUnlockImpl(LockHandle &l)
338{
339 l.unlockRead();
340}
341
342////////////////////////////////////////////////////////////////////////////////
343//
344// AutoWriteLockBase
345//
346////////////////////////////////////////////////////////////////////////////////
347
348/**
349 * Implementation of the pure virtual declared in AutoLockBase.
350 * This gets called by AutoLockBase.acquire() to actually request
351 * the semaphore; in the AutoWriteLock implementation, we request
352 * the semaphore in write mode.
353 */
354/*virtual*/ void AutoWriteLockBase::callLockImpl(LockHandle &l)
355{
356 l.lockWrite();
357}
358
359/**
360 * Implementation of the pure virtual declared in AutoLockBase.
361 * This gets called by AutoLockBase.release() to actually release
362 * the semaphore; in the AutoWriteLock implementation, we release
363 * the semaphore in write mode.
364 */
365/*virtual*/ void AutoWriteLockBase::callUnlockImpl(LockHandle &l)
366{
367 l.unlockWrite();
368}
369
370/**
371 * Causes the current thread to completely release the write lock to make
372 * the managed semaphore immediately available for locking by other threads.
373 *
374 * This implies that all nested write locks on the semaphore will be
375 * released, even those that were acquired through the calls to #lock()
376 * methods of all other AutoWriteLock/AutoReadLock instances managing the
377 * <b>same</b> read/write semaphore.
378 *
379 * After calling this method, the only method you are allowed to call is
380 * #enter(). It will acquire the write lock again and restore the same
381 * level of nesting as it had before calling #leave().
382 *
383 * If this instance is destroyed without calling #enter(), the destructor
384 * will try to restore the write lock level that existed when #leave() was
385 * called minus the number of nested #lock() calls made on this instance
386 * itself. This is done to preserve lock levels of other
387 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
388 * any). Tiis also means that the destructor may indefinitely block if a
389 * write or a read lock is owned by some other thread by that time.
390 */
391void AutoWriteLockBase::leave()
392{
393 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot leave()!"));
394
395 uint32_t i = 0;
396 for (HandlesVector::iterator it = m->aHandles.begin();
397 it != m->aHandles.end();
398 ++it)
399 {
400 LockHandle *pHandle = *it;
401 if (pHandle)
402 {
403 AssertMsg(m->acUnlockedInLeave[i] == 0, ("m->cUnlockedInLeave[%d] is %d, must be 0! Called leave() twice?", i, m->acUnlockedInLeave[i]));
404 m->acUnlockedInLeave[i] = pHandle->writeLockLevel();
405 AssertMsg(m->acUnlockedInLeave[i] >= 1, ("m->cUnlockedInLeave[%d] is %d, must be >=1!", i, m->acUnlockedInLeave[i]));
406
407 for (uint32_t left = m->acUnlockedInLeave[i];
408 left;
409 --left)
410 pHandle->unlockWrite();
411 }
412 ++i;
413 }
414}
415
416/**
417 * Causes the current thread to restore the write lock level after the
418 * #leave() call. This call will indefinitely block if another thread has
419 * successfully acquired a write or a read lock on the same semaphore in
420 * between.
421 */
422void AutoWriteLockBase::enter()
423{
424 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot enter()!"));
425
426 uint32_t i = 0;
427 for (HandlesVector::iterator it = m->aHandles.begin();
428 it != m->aHandles.end();
429 ++it)
430 {
431 LockHandle *pHandle = *it;
432 if (pHandle)
433 {
434 AssertMsg(m->acUnlockedInLeave[i] != 0, ("m->cUnlockedInLeave[%d] is 0! enter() without leave()?", i));
435
436 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
437 pHandle->lockWrite();
438 }
439 ++i;
440 }
441}
442
443/**
444 * Same as #leave() but checks if the current thread actally owns the lock
445 * and only proceeds in this case. As a result, as opposed to #leave(),
446 * doesn't assert when called with no lock being held.
447 */
448void AutoWriteLockBase::maybeLeave()
449{
450 uint32_t i = 0;
451 for (HandlesVector::iterator it = m->aHandles.begin();
452 it != m->aHandles.end();
453 ++it)
454 {
455 LockHandle *pHandle = *it;
456 if (pHandle)
457 {
458 if (pHandle->isWriteLockOnCurrentThread())
459 {
460 m->acUnlockedInLeave[i] = pHandle->writeLockLevel();
461 AssertMsg(m->acUnlockedInLeave[i] >= 1, ("m->cUnlockedInLeave[%d] is %d, must be >=1!", i, m->acUnlockedInLeave[i]));
462
463 for (uint32_t left = m->acUnlockedInLeave[i];
464 left;
465 --left)
466 pHandle->unlockWrite();
467 }
468 }
469 ++i;
470 }
471}
472
473/**
474 * Same as #enter() but checks if the current thread actally owns the lock
475 * and only proceeds if not. As a result, as opposed to #enter(), doesn't
476 * assert when called with the lock already being held.
477 */
478void AutoWriteLockBase::maybeEnter()
479{
480 uint32_t i = 0;
481 for (HandlesVector::iterator it = m->aHandles.begin();
482 it != m->aHandles.end();
483 ++it)
484 {
485 LockHandle *pHandle = *it;
486 if (pHandle)
487 {
488 if (!pHandle->isWriteLockOnCurrentThread())
489 {
490 for (; m->acUnlockedInLeave[i]; --m->acUnlockedInLeave[i])
491 pHandle->lockWrite();
492 }
493 }
494 ++i;
495 }
496}
497
498////////////////////////////////////////////////////////////////////////////////
499//
500// AutoWriteLock
501//
502////////////////////////////////////////////////////////////////////////////////
503
504/**
505 * Attaches another handle to this auto lock instance.
506 *
507 * The previous object's lock is completely released before the new one is
508 * acquired. The lock level of the new handle will be the same. This
509 * also means that if the lock was not acquired at all before #attach(), it
510 * will not be acquired on the new handle too.
511 *
512 * @param aHandle New handle to attach.
513 */
514void AutoWriteLock::attach(LockHandle *aHandle)
515{
516 LockHandle *pHandle = m->aHandles[0];
517
518 /* detect simple self-reattachment */
519 if (pHandle != aHandle)
520 {
521 bool fWasLocked = m->fIsLocked;
522
523 cleanup();
524
525 m->aHandles[0] = aHandle;
526 m->fIsLocked = fWasLocked;
527
528 if (aHandle)
529 if (fWasLocked)
530 aHandle->lockWrite();
531 }
532}
533
534void AutoWriteLock::attachRaw(LockHandle *ph)
535{
536 m->aHandles[0] = ph;
537}
538
539/**
540 * Returns @c true if the current thread holds a write lock on the managed
541 * read/write semaphore. Returns @c false if the managed semaphore is @c
542 * NULL.
543 *
544 * @note Intended for debugging only.
545 */
546bool AutoWriteLock::isWriteLockOnCurrentThread() const
547{
548 return m->aHandles[0] ? m->aHandles[0]->isWriteLockOnCurrentThread() : false;
549}
550
551 /**
552 * Returns the current write lock level of the managed smaphore. The lock
553 * level determines the number of nested #lock() calls on the given
554 * semaphore handle. Returns @c 0 if the managed semaphore is @c
555 * NULL.
556 *
557 * Note that this call is valid only when the current thread owns a write
558 * lock on the given semaphore handle and will assert otherwise.
559 *
560 * @note Intended for debugging only.
561 */
562uint32_t AutoWriteLock::writeLockLevel() const
563{
564 return m->aHandles[0] ? m->aHandles[0]->writeLockLevel() : 0;
565}
566
567////////////////////////////////////////////////////////////////////////////////
568//
569// AutoMultiWriteLock*
570//
571////////////////////////////////////////////////////////////////////////////////
572
573AutoMultiWriteLock2::AutoMultiWriteLock2(Lockable *pl1, Lockable *pl2)
574 : AutoWriteLockBase(2)
575{
576 if (pl1)
577 m->aHandles[0] = pl1->lockHandle();
578 if (pl2)
579 m->aHandles[1] = pl2->lockHandle();
580 acquire();
581}
582
583AutoMultiWriteLock2::AutoMultiWriteLock2(LockHandle *pl1, LockHandle *pl2)
584 : AutoWriteLockBase(2)
585{
586 m->aHandles[0] = pl1;
587 m->aHandles[1] = pl2;
588 acquire();
589}
590
591AutoMultiWriteLock3::AutoMultiWriteLock3(Lockable *pl1, Lockable *pl2, Lockable *pl3)
592 : AutoWriteLockBase(3)
593{
594 if (pl1)
595 m->aHandles[0] = pl1->lockHandle();
596 if (pl2)
597 m->aHandles[1] = pl2->lockHandle();
598 if (pl3)
599 m->aHandles[2] = pl3->lockHandle();
600 acquire();
601}
602
603AutoMultiWriteLock3::AutoMultiWriteLock3(LockHandle *pl1, LockHandle *pl2, LockHandle *pl3)
604 : AutoWriteLockBase(3)
605{
606 m->aHandles[0] = pl1;
607 m->aHandles[1] = pl2;
608 m->aHandles[2] = pl3;
609 acquire();
610}
611
612} /* namespace util */
613/* 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