VirtualBox

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

Last change on this file since 8684 was 8664, checked in by vboxsync, 17 years ago

Lock counting. Correct #else/#endif comments (gnu convention).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 14.2 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 Assert (mWriteLockLevel != 0 /* unlockWrite() w/o preceding lockWrite()? */);
294 if (mWriteLockLevel != 0)
295 {
296 -- mWriteLockLevel;
297 if (mWriteLockLevel == 0)
298 {
299 Assert (mSelfReadLockCount == 0
300 /* mixed unlockWrite()/unlockRead() order? */);
301
302 mWriteLockThread = NIL_RTNATIVETHREAD;
303
304 /* no write locks, let writers go if there are any (top priority),
305 * otherwise let readers go if there are any */
306 if (mWriteLockPending != 0)
307 RTSemEventSignal (mGoWriteSem);
308 else if (mReadLockCount != 0)
309 RTSemEventMultiSignal (mGoReadSem);
310
311# ifdef DEBUG
312 RTTHREAD iprtThreadSelf = RTThreadSelf();
313 if (iprtThreadSelf != NIL_RTTHREAD)
314 RTThreadWriteLockDec (iprtThreadSelf);
315# endif
316 }
317 }
318
319# ifdef VBOX_MAIN_AUTOLOCK_TRAP
320 logOp (UnlockWrite);
321# endif
322
323 RTCritSectLeave (&mCritSect);
324
325#endif /* !VBOX_MAIN_USE_SEMRW */
326}
327
328
329void RWLockHandle::lockRead()
330{
331#ifdef VBOX_MAIN_USE_SEMRW
332
333 int vrc = RTSemRWRequestRead (mSemRW, RT_INDEFINITE_WAIT);
334 AssertRC (vrc);
335
336#else /* !VBOX_MAIN_USE_SEMRW */
337
338 RTCritSectEnter (&mCritSect);
339
340 RTNATIVETHREAD threadSelf = RTThreadNativeSelf();
341
342# ifdef VBOX_MAIN_AUTOLOCK_TRAP
343 logOp (LockRead);
344# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
345
346 bool isWriteLock = mWriteLockLevel != 0;
347 bool isFirstReadLock = mReadLockCount == 0;
348
349 if (isWriteLock && mWriteLockThread == threadSelf)
350 {
351 /* read lock nested into the write lock */
352 ++ mSelfReadLockCount;
353 Assert (mSelfReadLockCount != 0 /* self read lock overflow? */);
354
355 /* cause to return immediately */
356 isWriteLock = false;
357 }
358 else
359 {
360 ++ mReadLockCount;
361 Assert (mReadLockCount != 0 /* read lock overflow? */);
362
363 if (!isWriteLock)
364 {
365 Assert (mSelfReadLockCount == 0 /* missing unlockRead()? */);
366
367 /* write locks are top priority, so let them go if they are
368 * pending */
369 if (mWriteLockPending != 0)
370 {
371 isWriteLock = true;
372 /* the first postponed reader kicks pending writers */
373 if (isFirstReadLock)
374 RTSemEventSignal (mGoWriteSem);
375 }
376 }
377
378 /* the first waiting reader resets the semaphore before letting it be
379 * posted (i.e. before leaving the critical section) */
380 if (isWriteLock && isFirstReadLock)
381 RTSemEventMultiReset (mGoReadSem);
382 }
383
384 RTCritSectLeave (&mCritSect);
385
386 /* wait until the write lock is released */
387 if (isWriteLock)
388 RTSemEventMultiWait (mGoReadSem, RT_INDEFINITE_WAIT);
389
390# ifdef DEBUG
391 RTTHREAD iprtThreadSelf = RTThreadSelf();
392 if (iprtThreadSelf != NIL_RTTHREAD)
393 RTThreadReadLockInc (iprtThreadSelf);
394# endif
395
396#endif /* !VBOX_MAIN_USE_SEMRW */
397}
398
399
400void RWLockHandle::unlockRead()
401{
402#ifdef VBOX_MAIN_USE_SEMRW
403
404 int vrc = RTSemRWReleaseRead (mSemRW);
405 AssertRC (vrc);
406
407#else /* !VBOX_MAIN_USE_SEMRW */
408
409 RTCritSectEnter (&mCritSect);
410
411 RTNATIVETHREAD threadSelf = RTThreadNativeSelf();
412
413 if (mWriteLockLevel != 0)
414 {
415 /* read unlock nested into the write lock */
416 Assert (mWriteLockThread == threadSelf
417 /* unlockRead() after lockWrite()? */);
418 if (mWriteLockThread == threadSelf)
419 {
420 Assert (mSelfReadLockCount != 0
421 /* unlockRead() w/o preceding lockRead()? */);
422 if (mSelfReadLockCount != 0)
423 {
424 -- mSelfReadLockCount;
425
426# ifdef VBOX_MAIN_AUTOLOCK_TRAP
427 logOp (UnlockRead);
428# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
429 }
430 }
431 }
432 else
433 {
434 Assert (mReadLockCount != 0
435 /* unlockRead() w/o preceding lockRead()? */);
436 if (mReadLockCount != 0)
437 {
438 -- mReadLockCount;
439 if (mReadLockCount == 0)
440 {
441 /* no read locks, let writers go if there are any */
442 if (mWriteLockPending != 0)
443 RTSemEventSignal (mGoWriteSem);
444 }
445
446# ifdef VBOX_MAIN_AUTOLOCK_TRAP
447 logOp (UnlockRead);
448# endif /* VBOX_MAIN_AUTOLOCK_TRAP */
449 }
450 }
451
452 RTCritSectLeave (&mCritSect);
453
454# ifdef DEBUG
455 RTTHREAD iprtThreadSelf = RTThreadSelf();
456 if (iprtThreadSelf != NIL_RTTHREAD)
457 RTThreadReadLockDec (iprtThreadSelf);
458# endif
459
460#endif /* !VBOX_MAIN_USE_SEMRW */
461}
462
463
464uint32_t RWLockHandle::writeLockLevel() const
465{
466#ifdef VBOX_MAIN_USE_SEMRW
467
468 return RTSemRWGetWriteRecursion (mSemRW);
469
470#else /* !VBOX_MAIN_USE_SEMRW */
471
472 Assert (mWriteLockLevel != 0);
473
474 return mWriteLockLevel;
475
476#endif /* !VBOX_MAIN_USE_SEMRW */
477}
478
479
480#ifdef VBOX_MAIN_AUTOLOCK_TRAP
481
482void RWLockHandle::logOp (Operation aOp)
483{
484 std::string info;
485
486 char buf [256];
487 RTStrPrintf (buf, sizeof (buf), "[%c] Thread %08x (%s)\n",
488 aOp == LockRead ? 'r' : aOp == LockWrite ? 'w' : '?',
489 RTThreadNativeSelf(), RTThreadGetName (RTThreadSelf()));
490 info += buf;
491
492# if defined (RT_OS_LINUX)
493
494 void *trace [16];
495 char **messages = (char **) NULL;
496 int i, trace_size = 0;
497 trace_size = backtrace (trace, 16);
498
499 messages = backtrace_symbols (trace, trace_size);
500 /* skip first stack frame (points here) and the second stack frame (points
501 * to lockRead()/lockWrite() */
502 for (i = 2; i < trace_size; ++i)
503 (info += messages[i]) += "\n";
504
505 free (messages);
506
507# endif /* defined (RT_OS_LINUX) */
508
509 internal::TLS *tls = internal::gGlobal.tls();
510 if (tls != NULL)
511 {
512
513 switch (aOp)
514 {
515 case LockRead:
516 {
517 mReaderInfo.push_back (info);
518 ++ tls->handles [this];
519 break;
520 }
521 case UnlockRead:
522 {
523 mReaderInfo.pop_back();
524 -- tls->handles [this];
525 break;
526 }
527 case LockWrite:
528 {
529 mWriterInfo = info;
530 ++ tls->handles [this];
531 break;
532 }
533 case UnlockWrite:
534 {
535 mWriterInfo.clear();;
536 -- tls->handles [this];
537 break;
538 }
539 }
540 }
541}
542
543void RWLockHandle::gatherInfo (std::string &aInfo)
544{
545 char buf [256];
546 RTStrPrintf (buf, sizeof (buf),
547 "[*] RWLockHandle %x:\n", this,
548 RTThreadNativeSelf(), RTThreadGetName (RTThreadSelf()));
549 aInfo += buf;
550
551 /* add reader info */
552 for (ReaderInfo::const_iterator it = mReaderInfo.begin();
553 it != mReaderInfo.end(); ++ it)
554 {
555 aInfo += *it;
556 }
557 /* add writer info */
558 if (!mWriterInfo.empty())
559 aInfo += mWriterInfo;
560}
561
562/* static */
563void RWLockHandle::TLSDestructor (internal::TLS *aTLS)
564{
565 using namespace internal;
566
567 if (aTLS != NULL && aTLS->handles.size())
568 {
569 std::string info;
570 size_t cnt = 0;
571
572 for (TLS::HandleMap::const_iterator it = aTLS->handles.begin();
573 it != aTLS->handles.end(); ++ it)
574 {
575 if (it->second.raw != 0)
576 {
577 it->first->gatherInfo (info);
578 ++ cnt;
579 }
580 }
581
582 if (cnt != 0)
583 {
584 AssertReleaseMsgFailed ((
585 "DETECTED %d HELD RWLockHandle's on Thread %08x!\n%s\n",
586 cnt, RTThreadNativeSelf(), info.c_str()));
587 }
588 }
589}
590
591#endif /* ifdef VBOX_MAIN_AUTOLOCK_TRAP */
592
593
594} /* namespace util */
595
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