VirtualBox

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

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

AutoLock.cpp: TLS question.

  • 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) /** @todo r=bird: Why only linux? All but windows supports destructors... */
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