VirtualBox

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

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

Main: preparation for deadlock detection: add AutoWriterLockBase in preparation of AutoMulti* conversion

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.4 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// AutoLockBase
94//
95////////////////////////////////////////////////////////////////////////////////
96
97typedef std::vector<LockHandle*> HandlesVector;
98
99struct AutoLockBase::Data
100{
101 Data(size_t cHandles)
102 : aHandles(cHandles), // size of array
103 fIsLocked(false),
104 cUnlockedInLeave(0)
105 { }
106
107 HandlesVector aHandles; // array (vector) of LockHandle instances; in the case of AutoWriteLock
108 // and AutoReadLock, there will only be one item on the list; with the
109 // AutoMulti* derivatives, there will be multiple
110 bool fIsLocked; // if true, then all items in aHandles are locked by this AutoLock and
111 // need to be unlocked in the destructor
112 uint32_t cUnlockedInLeave; // how many times the handle was unlocked in leave(); otherwise 0
113};
114
115AutoLockBase::AutoLockBase(LockHandle *pHandle)
116{
117 m = new Data(1);
118 m->aHandles[0] = pHandle;
119}
120
121AutoLockBase::~AutoLockBase()
122{
123 delete m;
124}
125
126/**
127 * Requests ownership of all contained lock handles by calling
128 * the pure virtual callLockImpl() function on each of them,
129 * which must be implemented by the descendant class; in the
130 * implementation, AutoWriteLock will request a write lock
131 * whereas AutoReadLock will request a read lock.
132 *
133 * Does *not* modify the lock counts in the member variables.
134 */
135void AutoLockBase::callLockOnAllHandles()
136{
137 for (HandlesVector::iterator it = m->aHandles.begin();
138 it != m->aHandles.end();
139 ++it)
140 {
141 LockHandle *pHandle = *it;
142 if (pHandle)
143 // call virtual function implemented in AutoWriteLock or AutoReadLock
144 this->callLockImpl(*pHandle);
145 }
146}
147
148/**
149 * Releases ownership of all contained lock handles by calling
150 * the pure virtual callUnlockImpl() function on each of them,
151 * which must be implemented by the descendant class; in the
152 * implementation, AutoWriteLock will release a write lock
153 * whereas AutoReadLock will release a read lock.
154 *
155 * Does *not* modify the lock counts in the member variables.
156 */
157void AutoLockBase::callUnlockOnAllHandles()
158{
159 for (HandlesVector::iterator it = m->aHandles.begin();
160 it != m->aHandles.end();
161 ++it)
162 {
163 LockHandle *pHandle = *it;
164 if (pHandle)
165 // call virtual function implemented in AutoWriteLock or AutoReadLock
166 this->callUnlockImpl(*pHandle);
167 }
168}
169
170/**
171 * Destructor implementation that can also be called explicitly, if required.
172 * Restores the exact state before the AutoLock was created; that is, unlocks
173 * all contained semaphores and might actually lock them again if leave()
174 * was called during the AutoLock's lifetime.
175 */
176void AutoLockBase::cleanup()
177{
178 if (m->cUnlockedInLeave)
179 {
180 // there was a leave() before the destruction: then restore the
181 // lock level that might have been set by locks other than our own
182 if (m->fIsLocked)
183 --m->cUnlockedInLeave; // no lock for our own
184 m->fIsLocked = false;
185 for (; m->cUnlockedInLeave; --m->cUnlockedInLeave)
186 callLockOnAllHandles();
187 }
188
189 if (m->fIsLocked)
190 callUnlockOnAllHandles();
191}
192
193/**
194 * Requests ownership of all contained semaphores. Public method that can
195 * only be called once and that also gets called by the AutoLock constructors.
196 */
197void AutoLockBase::acquire()
198{
199 AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
200 callLockOnAllHandles();
201 m->fIsLocked = true;
202}
203
204/**
205 * Releases ownership of all contained semaphores. Public method.
206 */
207void AutoLockBase::release()
208{
209 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
210 callUnlockOnAllHandles();
211 m->fIsLocked = false;
212}
213
214////////////////////////////////////////////////////////////////////////////////
215//
216// AutoReadLock
217//
218////////////////////////////////////////////////////////////////////////////////
219
220/**
221 * Release all read locks acquired by this instance through the #lock()
222 * call and destroys the instance.
223 *
224 * Note that if there there are nested #lock() calls without the
225 * corresponding number of #unlock() calls when the destructor is called, it
226 * will assert. This is because having an unbalanced number of nested locks
227 * is a program logic error which must be fixed.
228 */
229/*virtual*/ AutoReadLock::~AutoReadLock()
230{
231 LockHandle *pHandle = m->aHandles[0];
232
233 if (pHandle)
234 {
235 if (m->fIsLocked)
236 pHandle->unlockRead();
237 }
238}
239
240/**
241 * Implementation of the pure virtual declared in AutoLockBase.
242 * This gets called by AutoLockBase.acquire() to actually request
243 * the semaphore; in the AutoReadLock implementation, we request
244 * the semaphore in read mode.
245 */
246/*virtual*/ void AutoReadLock::callLockImpl(LockHandle &l)
247{
248 l.lockRead();
249}
250
251/**
252 * Implementation of the pure virtual declared in AutoLockBase.
253 * This gets called by AutoLockBase.release() to actually release
254 * the semaphore; in the AutoReadLock implementation, we release
255 * the semaphore in read mode.
256 */
257/*virtual*/ void AutoReadLock::callUnlockImpl(LockHandle &l)
258{
259 l.unlockRead();
260}
261
262////////////////////////////////////////////////////////////////////////////////
263//
264// AutoWriteLockBase
265//
266////////////////////////////////////////////////////////////////////////////////
267
268/**
269 * Implementation of the pure virtual declared in AutoLockBase.
270 * This gets called by AutoLockBase.acquire() to actually request
271 * the semaphore; in the AutoWriteLock implementation, we request
272 * the semaphore in write mode.
273 */
274/*virtual*/ void AutoWriteLockBase::callLockImpl(LockHandle &l)
275{
276 l.lockWrite();
277}
278
279/**
280 * Implementation of the pure virtual declared in AutoLockBase.
281 * This gets called by AutoLockBase.release() to actually release
282 * the semaphore; in the AutoWriteLock implementation, we release
283 * the semaphore in write mode.
284 */
285/*virtual*/ void AutoWriteLockBase::callUnlockImpl(LockHandle &l)
286{
287 l.unlockWrite();
288}
289
290/**
291 * Returns @c true if the current thread holds a write lock on the managed
292 * read/write semaphore. Returns @c false if the managed semaphore is @c
293 * NULL.
294 *
295 * @note Intended for debugging only.
296 */
297bool AutoWriteLockBase::isWriteLockOnCurrentThread() const
298{
299 return m->aHandles[0] ? m->aHandles[0]->isWriteLockOnCurrentThread() : false;
300}
301
302 /**
303 * Returns the current write lock level of the managed smaphore. The lock
304 * level determines the number of nested #lock() calls on the given
305 * semaphore handle. Returns @c 0 if the managed semaphore is @c
306 * NULL.
307 *
308 * Note that this call is valid only when the current thread owns a write
309 * lock on the given semaphore handle and will assert otherwise.
310 *
311 * @note Intended for debugging only.
312 */
313uint32_t AutoWriteLockBase::writeLockLevel() const
314{
315 return m->aHandles[0] ? m->aHandles[0]->writeLockLevel() : 0;
316}
317
318/**
319 * Causes the current thread to completely release the write lock to make
320 * the managed semaphore immediately available for locking by other threads.
321 *
322 * This implies that all nested write locks on the semaphore will be
323 * released, even those that were acquired through the calls to #lock()
324 * methods of all other AutoWriteLock/AutoReadLock instances managing the
325 * <b>same</b> read/write semaphore.
326 *
327 * After calling this method, the only method you are allowed to call is
328 * #enter(). It will acquire the write lock again and restore the same
329 * level of nesting as it had before calling #leave().
330 *
331 * If this instance is destroyed without calling #enter(), the destructor
332 * will try to restore the write lock level that existed when #leave() was
333 * called minus the number of nested #lock() calls made on this instance
334 * itself. This is done to preserve lock levels of other
335 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
336 * any). Tiis also means that the destructor may indefinitely block if a
337 * write or a read lock is owned by some other thread by that time.
338 */
339void AutoWriteLockBase::leave()
340{
341 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot leave()!"));
342 AssertMsg(m->cUnlockedInLeave == 0, ("m->cUnlockedInLeave is %d, must be 0! Called leave() twice?", m->cUnlockedInLeave));
343
344 for (HandlesVector::iterator it = m->aHandles.begin();
345 it != m->aHandles.end();
346 ++it)
347 {
348 LockHandle *pHandle = *it;
349 if (pHandle)
350 {
351 m->cUnlockedInLeave = pHandle->writeLockLevel();
352 AssertMsg(m->cUnlockedInLeave >= 1, ("m->cUnlockedInLeave is %d, must be >=1!", m->cUnlockedInLeave));
353
354 for (uint32_t left = m->cUnlockedInLeave;
355 left;
356 --left)
357 pHandle->unlockWrite();
358 }
359 }
360}
361
362/**
363 * Causes the current thread to restore the write lock level after the
364 * #leave() call. This call will indefinitely block if another thread has
365 * successfully acquired a write or a read lock on the same semaphore in
366 * between.
367 */
368void AutoWriteLockBase::enter()
369{
370 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot enter()!"));
371 AssertMsg(m->cUnlockedInLeave != 0, ("m->cUnlockedInLeave is 0! enter() without leave()?"));
372
373 for (HandlesVector::iterator it = m->aHandles.begin();
374 it != m->aHandles.end();
375 ++it)
376 {
377 LockHandle *pHandle = *it;
378 if (pHandle)
379 {
380 for (; m->cUnlockedInLeave; --m->cUnlockedInLeave)
381 pHandle->lockWrite();
382 }
383 }
384}
385
386////////////////////////////////////////////////////////////////////////////////
387//
388// AutoWriteLock
389//
390////////////////////////////////////////////////////////////////////////////////
391
392/**
393 * Attaches another handle to this auto lock instance.
394 *
395 * The previous object's lock is completely released before the new one is
396 * acquired. The lock level of the new handle will be the same. This
397 * also means that if the lock was not acquired at all before #attach(), it
398 * will not be acquired on the new handle too.
399 *
400 * @param aHandle New handle to attach.
401 */
402void AutoWriteLock::attach(LockHandle *aHandle)
403{
404 LockHandle *pHandle = m->aHandles[0];
405
406 /* detect simple self-reattachment */
407 if (pHandle != aHandle)
408 {
409 bool fWasLocked = m->fIsLocked;
410
411 cleanup();
412
413 m->aHandles[0] = aHandle;
414 m->fIsLocked = fWasLocked;
415
416 if (aHandle)
417 if (fWasLocked)
418 aHandle->lockWrite();
419 }
420}
421
422void AutoWriteLock::attachRaw(LockHandle *ph)
423{
424 m->aHandles[0] = ph;
425}
426
427} /* namespace util */
428/* 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