VirtualBox

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

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

Main: preparation for deadlock detection: make AutoLockBase data private, move from header

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.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#include "AutoLock.h"
23
24#include "Logging.h"
25
26#include <iprt/string.h>
27
28
29namespace util
30{
31
32////////////////////////////////////////////////////////////////////////////////
33//
34// RWLockHandle
35//
36////////////////////////////////////////////////////////////////////////////////
37
38RWLockHandle::RWLockHandle()
39{
40 int vrc = RTSemRWCreate (&mSemRW);
41 AssertRC (vrc);
42}
43
44
45RWLockHandle::~RWLockHandle()
46{
47 RTSemRWDestroy (mSemRW);
48}
49
50
51bool RWLockHandle::isWriteLockOnCurrentThread() const
52{
53 return RTSemRWIsWriteOwner (mSemRW);
54}
55
56
57void RWLockHandle::lockWrite()
58{
59 int vrc = RTSemRWRequestWrite (mSemRW, RT_INDEFINITE_WAIT);
60 AssertRC (vrc);
61}
62
63
64void RWLockHandle::unlockWrite()
65{
66 int vrc = RTSemRWReleaseWrite (mSemRW);
67 AssertRC (vrc);
68}
69
70
71void RWLockHandle::lockRead()
72{
73 int vrc = RTSemRWRequestRead (mSemRW, RT_INDEFINITE_WAIT);
74 AssertRC (vrc);
75}
76
77
78void RWLockHandle::unlockRead()
79{
80 int vrc = RTSemRWReleaseRead (mSemRW);
81 AssertRC (vrc);
82}
83
84
85uint32_t RWLockHandle::writeLockLevel() const
86{
87 return RTSemRWGetWriteRecursion (mSemRW);
88}
89
90////////////////////////////////////////////////////////////////////////////////
91//
92// AutoLockBase
93//
94////////////////////////////////////////////////////////////////////////////////
95
96struct AutoLockBase::Data
97{
98 Data(LockHandle *argpHandle)
99 : pHandle(argpHandle),
100 fIsLocked(false),
101 cUnlockedInLeave(0)
102 { }
103
104 LockHandle *pHandle;
105 bool fIsLocked;
106 uint32_t cUnlockedInLeave; // how many times the handle was unlocked in leave(); otherwise 0
107};
108
109AutoLockBase::AutoLockBase(LockHandle *pHandle)
110{
111 m = new Data(pHandle);
112}
113
114AutoLockBase::~AutoLockBase()
115{
116 delete m;
117}
118
119////////////////////////////////////////////////////////////////////////////////
120//
121// AutoWriteLock
122//
123////////////////////////////////////////////////////////////////////////////////
124
125void AutoWriteLock::cleanup()
126{
127 if (m->pHandle)
128 {
129 if (m->cUnlockedInLeave)
130 {
131 // there was a leave() before the destruction: then restore the
132 // lock level that might have been set by locks other than our own
133 if (m->fIsLocked)
134 --m->cUnlockedInLeave; // no lock for our own
135 m->fIsLocked = false;
136 for (; m->cUnlockedInLeave; --m->cUnlockedInLeave)
137 m->pHandle->lockWrite();
138
139 // @todo r=dj is this really desirable behavior? maybe leave/enter should go altogether?
140 }
141
142 if (m->fIsLocked)
143 m->pHandle->unlockWrite();
144 }
145}
146
147void AutoWriteLock::acquire()
148{
149 if (m->pHandle)
150 {
151 AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
152 m->pHandle->lockWrite();
153 m->fIsLocked = true;
154 }
155}
156
157void AutoWriteLock::release()
158{
159 if (m->pHandle)
160 {
161 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
162 m->pHandle->unlockWrite();
163 m->fIsLocked = false;
164 }
165}
166
167/**
168 * Causes the current thread to completely release the write lock to make
169 * the managed semaphore immediately available for locking by other threads.
170 *
171 * This implies that all nested write locks on the semaphore will be
172 * released, even those that were acquired through the calls to #lock()
173 * methods of all other AutoWriteLock/AutoReadLock instances managing the
174 * <b>same</b> read/write semaphore.
175 *
176 * After calling this method, the only method you are allowed to call is
177 * #enter(). It will acquire the write lock again and restore the same
178 * level of nesting as it had before calling #leave().
179 *
180 * If this instance is destroyed without calling #enter(), the destructor
181 * will try to restore the write lock level that existed when #leave() was
182 * called minus the number of nested #lock() calls made on this instance
183 * itself. This is done to preserve lock levels of other
184 * AutoWriteLock/AutoReadLock instances managing the same semaphore (if
185 * any). Tiis also means that the destructor may indefinitely block if a
186 * write or a read lock is owned by some other thread by that time.
187 */
188void AutoWriteLock::leave()
189{
190 if (m->pHandle)
191 {
192 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot leave()!"));
193 AssertMsg(m->cUnlockedInLeave == 0, ("m->cUnlockedInLeave is %d, must be 0! Called leave() twice?", m->cUnlockedInLeave));
194
195 m->cUnlockedInLeave = m->pHandle->writeLockLevel();
196 AssertMsg(m->cUnlockedInLeave >= 1, ("m->cUnlockedInLeave is %d, must be >=1!", m->cUnlockedInLeave));
197
198 for (uint32_t left = m->cUnlockedInLeave;
199 left;
200 --left)
201 m->pHandle->unlockWrite();
202 }
203}
204
205/**
206 * Causes the current thread to restore the write lock level after the
207 * #leave() call. This call will indefinitely block if another thread has
208 * successfully acquired a write or a read lock on the same semaphore in
209 * between.
210 */
211void AutoWriteLock::enter()
212{
213 if (m->pHandle)
214 {
215 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot enter()!"));
216 AssertMsg(m->cUnlockedInLeave != 0, ("m->cUnlockedInLeave is 0! enter() without leave()?"));
217
218 for (; m->cUnlockedInLeave; --m->cUnlockedInLeave)
219 m->pHandle->lockWrite();
220 }
221}
222
223/**
224 * Attaches another handle to this auto lock instance.
225 *
226 * The previous object's lock is completely released before the new one is
227 * acquired. The lock level of the new handle will be the same. This
228 * also means that if the lock was not acquired at all before #attach(), it
229 * will not be acquired on the new handle too.
230 *
231 * @param aHandle New handle to attach.
232 */
233void AutoWriteLock::attach(LockHandle *aHandle)
234{
235 /* detect simple self-reattachment */
236 if (m->pHandle != aHandle)
237 {
238 bool fWasLocked = m->fIsLocked;
239
240 cleanup();
241
242 m->pHandle = aHandle;
243 m->fIsLocked = fWasLocked;
244
245 if (m->pHandle)
246 if (fWasLocked)
247 m->pHandle->lockWrite();
248 }
249}
250
251void AutoWriteLock::attachRaw(LockHandle *ph)
252{
253 m->pHandle = ph;
254}
255
256/**
257 * Returns @c true if the current thread holds a write lock on the managed
258 * read/write semaphore. Returns @c false if the managed semaphore is @c
259 * NULL.
260 *
261 * @note Intended for debugging only.
262 */
263bool AutoWriteLock::isWriteLockOnCurrentThread() const
264{
265 return m->pHandle ? m->pHandle->isWriteLockOnCurrentThread() : false;
266}
267
268 /**
269 * Returns the current write lock level of the managed smaphore. The lock
270 * level determines the number of nested #lock() calls on the given
271 * semaphore handle. Returns @c 0 if the managed semaphore is @c
272 * NULL.
273 *
274 * Note that this call is valid only when the current thread owns a write
275 * lock on the given semaphore handle and will assert otherwise.
276 *
277 * @note Intended for debugging only.
278 */
279uint32_t AutoWriteLock::writeLockLevel() const
280{
281 return m->pHandle ? m->pHandle->writeLockLevel() : 0;
282}
283
284////////////////////////////////////////////////////////////////////////////////
285//
286// AutoReadLock
287//
288////////////////////////////////////////////////////////////////////////////////
289
290/**
291 * Release all read locks acquired by this instance through the #lock()
292 * call and destroys the instance.
293 *
294 * Note that if there there are nested #lock() calls without the
295 * corresponding number of #unlock() calls when the destructor is called, it
296 * will assert. This is because having an unbalanced number of nested locks
297 * is a program logic error which must be fixed.
298 */
299/*virtual*/ AutoReadLock::~AutoReadLock()
300{
301 if (m->pHandle)
302 {
303 if (m->fIsLocked)
304 m->pHandle->unlockRead();
305 }
306}
307
308/**
309 * Requests a read (shared) lock. If a read lock is already owned by
310 * this thread, increases the lock level (allowing for nested read locks on
311 * the same thread). Blocks indefinitely if a write lock is already owned by
312 * another thread until that tread releases the write lock, otherwise
313 * returns immediately.
314 *
315 * Note that this method returns immediately even if any number of other
316 * threads owns read locks on the same semaphore. Also returns immediately
317 * if a write lock on this semaphore is owned by the current thread which
318 * allows for read locks nested into write locks on the same thread.
319 */
320void AutoReadLock::acquire()
321{
322 if (m->pHandle)
323 {
324 AssertMsg(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
325 m->pHandle->lockRead();
326 m->fIsLocked = true;
327 }
328}
329
330/**
331 * Decreases the read lock level increased by #lock(). If the level drops to
332 * zero (e.g. the number of nested #unlock() calls matches the number of
333 * nested #lock() calls), releases the lock making the managed semaphore
334 * available for locking by other threads.
335 */
336void AutoReadLock::release()
337{
338 if (m->pHandle)
339 {
340 AssertMsg(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
341 m->pHandle->unlockRead();
342 m->fIsLocked = false;
343 }
344}
345
346
347} /* namespace util */
348/* 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