VirtualBox

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

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

Main/AutoLock: More debugging.

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