VirtualBox

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

Last change on this file since 14772 was 14772, checked in by vboxsync, 16 years ago

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 14.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#ifdef VBOX_MAIN_AUTOLOCK_TRAP
23// workaround for compile problems on gcc 4.1
24# ifdef __GNUC__
25# pragma GCC visibility push(default)
26# endif
27#endif
28
29#include "AutoLock.h"
30
31#include "Logging.h"
32
33#include <iprt/string.h>
34
35#ifdef VBOX_MAIN_AUTOLOCK_TRAP
36# if defined (RT_OS_LINUX)
37# include <signal.h>
38# include <execinfo.h>
39/* get REG_EIP from ucontext.h */
40# ifndef __USE_GNU
41# define __USE_GNU
42# endif
43# include <ucontext.h>
44# ifdef RT_ARCH_AMD64
45# define REG_PC REG_RIP
46# else
47# define REG_PC REG_EIP
48# endif
49# endif
50#endif /* VBOX_MAIN_AUTOLOCK_TRAP */
51
52namespace util
53{
54
55#ifdef VBOX_MAIN_AUTOLOCK_TRAP
56
57namespace internal
58{
59
60struct TLS
61{
62 struct Uint32_t
63 {
64 Uint32_t() : raw (0) {}
65 operator uint32_t &() { return raw; }
66 uint32_t raw;
67 };
68
69 typedef std::map <RWLockHandle *, Uint32_t> HandleMap;
70 HandleMap handles; /*< handle reference counter on the current thread */
71};
72
73/**
74 * Global module initialization structure.
75 *
76 * The constructor and destructor of this structure are used to perform global
77 * module initiaizaton and cleanup. Thee must be only one global variable of
78 * this structure.
79 */
80static
81class Global
82{
83public:
84
85 Global() : tlsID (NIL_RTTLS)
86 {
87#if defined (RT_OS_LINUX)
88 int vrc = RTTlsAllocEx (&tlsID, TLSDestructor);
89 AssertRC (vrc);
90#else
91 tlsID = RTTlsAlloc();
92 Assert (tlsID != NIL_RTTLS);
93#endif
94 }
95
96 ~Global()
97 {
98 RTTlsFree (tlsID);
99 }
100
101 TLS *tls() const
102 {
103 TLS *tls = NULL;
104 if (tlsID != NIL_RTTLS)
105 {
106 tls = static_cast <TLS *> (RTTlsGet (tlsID));
107 if (tls == NULL)
108 {
109 tls = new TLS();
110 RTTlsSet (tlsID, tls);
111 }
112 }
113
114 return tls;
115 }
116
117 RTTLS tlsID;
118}
119gGlobal;
120
121DECLCALLBACK(void) TLSDestructor (void *aValue)
122{
123 if (aValue != NULL)
124 {
125 TLS *tls = static_cast <TLS *> (aValue);
126 RWLockHandle::TLSDestructor (tls);
127 delete tls;
128 }
129}
130
131} /* namespace internal */
132
133#endif /* VBOX_MAIN_AUTOLOCK_TRAP */
134
135
136RWLockHandle::RWLockHandle()
137{
138#ifdef VBOX_MAIN_USE_SEMRW
139
140 int vrc = RTSemRWCreate (&mSemRW);
141 AssertRC (vrc);
142
143#else /* !VBOX_MAIN_USE_SEMRW */
144
145 int vrc = RTCritSectInit (&mCritSect);
146 AssertRC (vrc);
147 vrc = RTSemEventCreate (&mGoWriteSem);
148 AssertRC (vrc);
149 vrc = RTSemEventMultiCreate (&mGoReadSem);
150 AssertRC (vrc);
151
152 mWriteLockThread = NIL_RTNATIVETHREAD;
153
154 mReadLockCount = 0;
155 mSelfReadLockCount = 0;
156
157 mWriteLockLevel = 0;
158 mWriteLockPending = 0;
159
160#endif /* !VBOX_MAIN_USE_SEMRW */
161}
162
163
164RWLockHandle::~RWLockHandle()
165{
166#ifdef VBOX_MAIN_USE_SEMRW
167
168 RTSemRWDestroy (mSemRW);
169
170#else /* !VBOX_MAIN_USE_SEMRW */
171
172 RTSemEventMultiDestroy (mGoReadSem);
173 RTSemEventDestroy (mGoWriteSem);
174 RTCritSectDelete (&mCritSect);
175
176#endif /* !VBOX_MAIN_USE_SEMRW */
177}
178
179
180bool RWLockHandle::isWriteLockOnCurrentThread() const
181{
182#ifdef VBOX_MAIN_USE_SEMRW
183
184 return RTSemRWIsWriteOwner (mSemRW);
185
186#else /* !VBOX_MAIN_USE_SEMRW */
187
188 RTCritSectEnter (&mCritSect);
189 bool locked = mWriteLockThread == RTThreadNativeSelf();
190 RTCritSectLeave (&mCritSect);
191 return locked;
192
193#endif /* !VBOX_MAIN_USE_SEMRW */
194}
195
196
197void RWLockHandle::lockWrite()
198{
199#ifdef VBOX_MAIN_USE_SEMRW
200
201 int vrc = RTSemRWRequestWrite (mSemRW, RT_INDEFINITE_WAIT);
202 AssertRC (vrc);
203
204#else /* !VBOX_MAIN_USE_SEMRW */
205
206 RTCritSectEnter (&mCritSect);
207
208 RTNATIVETHREAD threadSelf = RTThreadNativeSelf();
209
210 if (mWriteLockThread != threadSelf)
211 {
212# ifdef VBOX_MAIN_AUTOLOCK_TRAP
213 if (mReadLockCount != 0)
214 {
215 using namespace internal;
216 TLS *tls = gGlobal.tls();
217 if (tls != NULL)
218 {
219 TLS::HandleMap::const_iterator it = tls->handles.find (this);
220 if (it != tls->handles.end() && it->second.raw != 0)
221 {
222 /* if there is a writer then the handle reference counter equals
223 * to the number of readers on the current thread plus 1 */
224
225 uint32_t readers = it->second.raw;
226 if (mWriteLockThread != NIL_RTNATIVETHREAD)
227 -- readers;
228
229 std::string info;
230 gatherInfo (info);
231
232 AssertReleaseMsgFailedReturnVoid ((
233 "DETECTED SELF DEADLOCK on Thread %08x: lockWrite() after "
234 "lockRead(): reader count = %d!\n%s\n",
235 threadSelf, readers, info.c_str()));
236 }
237 }
238 }
239# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
240
241 if (mReadLockCount != 0 || mWriteLockThread != NIL_RTNATIVETHREAD ||
242 mWriteLockPending != 0 /* respect other pending writers */)
243 {
244 /* wait until all read locks or another write lock is released */
245 ++ mWriteLockPending;
246 Assert (mWriteLockPending != 0 /* pending writer overflow? */);
247 RTCritSectLeave (&mCritSect);
248 RTSemEventWait (mGoWriteSem, RT_INDEFINITE_WAIT);
249 RTCritSectEnter (&mCritSect);
250 -- mWriteLockPending;
251 }
252
253 Assert (mWriteLockLevel == 0);
254 Assert (mWriteLockThread == NIL_RTNATIVETHREAD);
255 Assert (mSelfReadLockCount == 0 /* missing unlockRead()? */);
256
257 mWriteLockThread = threadSelf;
258 }
259
260 ++ mWriteLockLevel;
261 Assert (mWriteLockLevel != 0 /* overflow */);
262
263# ifdef VBOX_MAIN_AUTOLOCK_TRAP
264 logOp (LockWrite);
265# endif
266
267 RTCritSectLeave (&mCritSect);
268
269# ifdef DEBUG
270 if (mWriteLockLevel == 1)
271 {
272 RTTHREAD iprtThreadSelf = RTThreadSelf();
273 if (iprtThreadSelf != NIL_RTTHREAD)
274 RTThreadWriteLockInc (iprtThreadSelf);
275 }
276# endif
277
278#endif /* !VBOX_MAIN_USE_SEMRW */
279}
280
281
282void RWLockHandle::unlockWrite()
283{
284#ifdef VBOX_MAIN_USE_SEMRW
285
286 int vrc = RTSemRWReleaseWrite (mSemRW);
287 AssertRC (vrc);
288
289#else /* !VBOX_MAIN_USE_SEMRW */
290
291 RTCritSectEnter (&mCritSect);
292
293 RTNATIVETHREAD threadSelf = RTThreadNativeSelf();
294
295 Assert (mWriteLockLevel != 0 /* unlockWrite() w/o preceding lockWrite()? */);
296 if (mWriteLockLevel != 0)
297 {
298 Assert (mWriteLockThread == threadSelf
299 /* unlockWrite() w/o preceding lockWrite()? */);
300 if (mWriteLockThread == threadSelf)
301 {
302 -- mWriteLockLevel;
303 if (mWriteLockLevel == 0)
304 {
305 Assert (mSelfReadLockCount == 0
306 /* mixed unlockWrite()/unlockRead() order? */);
307
308 mWriteLockThread = NIL_RTNATIVETHREAD;
309
310 /* no write locks, let writers go if there are any (top priority),
311 * otherwise let readers go if there are any */
312 if (mWriteLockPending != 0)
313 RTSemEventSignal (mGoWriteSem);
314 else if (mReadLockCount != 0)
315 RTSemEventMultiSignal (mGoReadSem);
316
317# ifdef DEBUG
318 RTTHREAD iprtThreadSelf = RTThreadSelf();
319 if (iprtThreadSelf != NIL_RTTHREAD)
320 RTThreadWriteLockDec (iprtThreadSelf);
321# endif
322 }
323 }
324 }
325
326# ifdef VBOX_MAIN_AUTOLOCK_TRAP
327 logOp (UnlockWrite);
328# endif
329
330 RTCritSectLeave (&mCritSect);
331
332#endif /* !VBOX_MAIN_USE_SEMRW */
333}
334
335
336void RWLockHandle::lockRead()
337{
338#ifdef VBOX_MAIN_USE_SEMRW
339
340 int vrc = RTSemRWRequestRead (mSemRW, RT_INDEFINITE_WAIT);
341 AssertRC (vrc);
342
343#else /* !VBOX_MAIN_USE_SEMRW */
344
345 RTCritSectEnter (&mCritSect);
346
347 RTNATIVETHREAD threadSelf = RTThreadNativeSelf();
348
349# ifdef VBOX_MAIN_AUTOLOCK_TRAP
350 logOp (LockRead);
351# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
352
353 bool isWriteLock = mWriteLockLevel != 0;
354 bool isFirstReadLock = mReadLockCount == 0;
355
356 if (isWriteLock && mWriteLockThread == threadSelf)
357 {
358 /* read lock nested into the write lock */
359 ++ mSelfReadLockCount;
360 Assert (mSelfReadLockCount != 0 /* self read lock overflow? */);
361
362 /* cause to return immediately */
363 isWriteLock = false;
364 }
365 else
366 {
367 ++ mReadLockCount;
368 Assert (mReadLockCount != 0 /* read lock overflow? */);
369
370 if (!isWriteLock)
371 {
372 Assert (mSelfReadLockCount == 0 /* missing unlockRead()? */);
373
374 /* write locks are top priority, so let them go if they are
375 * pending and we're the only reader so far */
376 if (mWriteLockPending != 0 && isFirstReadLock)
377 {
378 isWriteLock = true;
379 /* note that we must not signal mGoWriteSem here because it
380 * has been already signaled by unlockWrite() or by
381 * unlockRead() */
382 }
383 }
384
385 /* the first waiting reader resets the semaphore before letting it be
386 * posted (i.e. before leaving the critical section) */
387 if (isWriteLock && isFirstReadLock)
388 RTSemEventMultiReset (mGoReadSem);
389 }
390
391 RTCritSectLeave (&mCritSect);
392
393 /* wait until the write lock is released */
394 if (isWriteLock)
395 RTSemEventMultiWait (mGoReadSem, RT_INDEFINITE_WAIT);
396
397# ifdef DEBUG
398 RTTHREAD iprtThreadSelf = RTThreadSelf();
399 if (iprtThreadSelf != NIL_RTTHREAD)
400 RTThreadReadLockInc (iprtThreadSelf);
401# endif
402
403#endif /* !VBOX_MAIN_USE_SEMRW */
404}
405
406
407void RWLockHandle::unlockRead()
408{
409#ifdef VBOX_MAIN_USE_SEMRW
410
411 int vrc = RTSemRWReleaseRead (mSemRW);
412 AssertRC (vrc);
413
414#else /* !VBOX_MAIN_USE_SEMRW */
415
416 RTCritSectEnter (&mCritSect);
417
418 RTNATIVETHREAD threadSelf = RTThreadNativeSelf();
419
420 if (mWriteLockLevel != 0)
421 {
422 /* read unlock nested into the write lock */
423 Assert (mWriteLockThread == threadSelf
424 /* unlockRead() after lockWrite()? */);
425 if (mWriteLockThread == threadSelf)
426 {
427 Assert (mSelfReadLockCount != 0
428 /* unlockRead() w/o preceding lockRead()? */);
429 if (mSelfReadLockCount != 0)
430 {
431 -- mSelfReadLockCount;
432
433# ifdef VBOX_MAIN_AUTOLOCK_TRAP
434 logOp (UnlockRead);
435# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
436 }
437 }
438 }
439 else
440 {
441 Assert (mReadLockCount != 0
442 /* unlockRead() w/o preceding lockRead()? */);
443 if (mReadLockCount != 0)
444 {
445 -- mReadLockCount;
446 if (mReadLockCount == 0)
447 {
448 /* no read locks, let writers go if there are any */
449 if (mWriteLockPending != 0)
450 RTSemEventSignal (mGoWriteSem);
451 }
452
453# ifdef VBOX_MAIN_AUTOLOCK_TRAP
454 logOp (UnlockRead);
455# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
456 }
457 }
458
459 RTCritSectLeave (&mCritSect);
460
461# ifdef DEBUG
462 RTTHREAD iprtThreadSelf = RTThreadSelf();
463 if (iprtThreadSelf != NIL_RTTHREAD)
464 RTThreadReadLockDec (iprtThreadSelf);
465# endif
466
467#endif /* !VBOX_MAIN_USE_SEMRW */
468}
469
470
471uint32_t RWLockHandle::writeLockLevel() const
472{
473#ifdef VBOX_MAIN_USE_SEMRW
474
475 return RTSemRWGetWriteRecursion (mSemRW);
476
477#else /* !VBOX_MAIN_USE_SEMRW */
478
479 Assert (mWriteLockLevel != 0);
480
481 return mWriteLockLevel;
482
483#endif /* !VBOX_MAIN_USE_SEMRW */
484}
485
486
487#ifdef VBOX_MAIN_AUTOLOCK_TRAP
488
489void RWLockHandle::logOp (Operation aOp)
490{
491 std::string info;
492
493 char buf [256];
494 RTStrPrintf (buf, sizeof (buf), "[%c] Thread %08x (%s)\n",
495 aOp == LockRead ? 'r' : aOp == LockWrite ? 'w' : '?',
496 RTThreadNativeSelf(), RTThreadGetName (RTThreadSelf()));
497 info += buf;
498
499# if defined (RT_OS_LINUX)
500
501 void *trace [16];
502 char **messages = (char **) NULL;
503 int i, trace_size = 0;
504 trace_size = backtrace (trace, 16);
505
506 messages = backtrace_symbols (trace, trace_size);
507 /* skip first stack frame (points here) and the second stack frame (points
508 * to lockRead()/lockWrite() */
509 for (i = 2; i < trace_size; ++i)
510 (info += messages[i]) += "\n";
511
512 free (messages);
513
514# endif /* defined (RT_OS_LINUX) */
515
516 internal::TLS *tls = internal::gGlobal.tls();
517 if (tls != NULL)
518 {
519
520 switch (aOp)
521 {
522 case LockRead:
523 {
524 mReaderInfo.push_back (info);
525 ++ tls->handles [this];
526 break;
527 }
528 case UnlockRead:
529 {
530 mReaderInfo.pop_back();
531 -- tls->handles [this];
532 break;
533 }
534 case LockWrite:
535 {
536 mWriterInfo = info;
537 ++ tls->handles [this];
538 break;
539 }
540 case UnlockWrite:
541 {
542 mWriterInfo.clear();;
543 -- tls->handles [this];
544 break;
545 }
546 }
547 }
548}
549
550void RWLockHandle::gatherInfo (std::string &aInfo)
551{
552 char buf [256];
553 RTStrPrintf (buf, sizeof (buf),
554 "[*] RWLockHandle %x:\n", this,
555 RTThreadNativeSelf(), RTThreadGetName (RTThreadSelf()));
556 aInfo += buf;
557
558 /* add reader info */
559 for (ReaderInfo::const_iterator it = mReaderInfo.begin();
560 it != mReaderInfo.end(); ++ it)
561 {
562 aInfo += *it;
563 }
564 /* add writer info */
565 if (!mWriterInfo.empty())
566 aInfo += mWriterInfo;
567}
568
569/* static */
570void RWLockHandle::TLSDestructor (internal::TLS *aTLS)
571{
572 using namespace internal;
573
574 if (aTLS != NULL && aTLS->handles.size())
575 {
576 std::string info;
577 size_t cnt = 0;
578
579 for (TLS::HandleMap::const_iterator it = aTLS->handles.begin();
580 it != aTLS->handles.end(); ++ it)
581 {
582 if (it->second.raw != 0)
583 {
584 it->first->gatherInfo (info);
585 ++ cnt;
586 }
587 }
588
589 if (cnt != 0)
590 {
591 AssertReleaseMsgFailed ((
592 "DETECTED %d HELD RWLockHandle's on Thread %08x!\n%s\n",
593 cnt, RTThreadNativeSelf(), info.c_str()));
594 }
595 }
596}
597
598#endif /* ifdef VBOX_MAIN_AUTOLOCK_TRAP */
599
600
601} /* namespace util */
602/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use