VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/lockvalidator.cpp@ 25833

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

lockvalidator.cpp: show the class+sub-class in the lock dump.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 152.0 KB
Line 
1/* $Id: lockvalidator.cpp 25833 2010-01-14 15:56:16Z vboxsync $ */
2/** @file
3 * IPRT - Lock Validator.
4 */
5
6/*
7 * Copyright (C) 2009-2010 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include <iprt/lockvalidator.h>
35#include "internal/iprt.h"
36
37#include <iprt/asm.h>
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/mem.h>
41#include <iprt/once.h>
42#include <iprt/semaphore.h>
43#include <iprt/string.h>
44#include <iprt/thread.h>
45
46#include "internal/lockvalidator.h"
47#include "internal/magics.h"
48#include "internal/thread.h"
49
50/*******************************************************************************
51* Defined Constants And Macros *
52*******************************************************************************/
53/** Macro that asserts that a pointer is aligned correctly.
54 * Only used when fighting bugs. */
55#if 1
56# define RTLOCKVAL_ASSERT_PTR_ALIGN(p) \
57 AssertMsg(!((uintptr_t)(p) & (sizeof(uintptr_t) - 1)), ("%p\n", (p)));
58#else
59# define RTLOCKVAL_ASSERT_PTR_ALIGN(p) do { } while (0)
60#endif
61
62/** Hashes the class handle (pointer) into an apPriorLocksHash index. */
63#define RTLOCKVALCLASS_HASH(hClass) \
64 ( ((uintptr_t)(hClass) >> 6 ) \
65 % ( RT_SIZEOFMEMB(RTLOCKVALCLASSINT, apPriorLocksHash) \
66 / sizeof(PRTLOCKVALCLASSREF)) )
67
68/** The max value for RTLOCKVALCLASSINT::cRefs. */
69#define RTLOCKVALCLASS_MAX_REFS UINT32_C(0xffff0000)
70/** The max value for RTLOCKVALCLASSREF::cLookups. */
71#define RTLOCKVALCLASSREF_MAX_LOOKUPS UINT32_C(0xfffe0000)
72/** The absolute max value for RTLOCKVALCLASSREF::cLookups at which it will
73 * be set back to RTLOCKVALCLASSREF_MAX_LOOKUPS. */
74#define RTLOCKVALCLASSREF_MAX_LOOKUPS_FIX UINT32_C(0xffff0000)
75
76
77/** @def RTLOCKVAL_WITH_RECURSION_RECORDS
78 * Enable recursion records. */
79#if defined(IN_RING3) || defined(DOXYGEN_RUNNING)
80# define RTLOCKVAL_WITH_RECURSION_RECORDS 1
81#endif
82
83/** @def RTLOCKVAL_WITH_VERBOSE_DUMPS
84 * Enables some extra verbosity in the lock dumping. */
85#if defined(DOXYGEN_RUNNING)
86# define RTLOCKVAL_WITH_VERBOSE_DUMPS
87#endif
88
89/** @def RTLOCKVAL_WITH_CLASS_HASH_STATS
90 * Enables collection prior class hash lookup statistics, dumping them when
91 * complaining about the class. */
92#if defined(DEBUG) || defined(DOXYGEN_RUNNING)
93# define RTLOCKVAL_WITH_CLASS_HASH_STATS
94#endif
95
96
97/*******************************************************************************
98* Structures and Typedefs *
99*******************************************************************************/
100/**
101 * Deadlock detection stack entry.
102 */
103typedef struct RTLOCKVALDDENTRY
104{
105 /** The current record. */
106 PRTLOCKVALRECUNION pRec;
107 /** The current entry number if pRec is a shared one. */
108 uint32_t iEntry;
109 /** The thread state of the thread we followed to get to pFirstSibling.
110 * This is only used for validating a deadlock stack. */
111 RTTHREADSTATE enmState;
112 /** The thread we followed to get to pFirstSibling.
113 * This is only used for validating a deadlock stack. */
114 PRTTHREADINT pThread;
115 /** What pThread is waiting on, i.e. where we entered the circular list of
116 * siblings. This is used for validating a deadlock stack as well as
117 * terminating the sibling walk. */
118 PRTLOCKVALRECUNION pFirstSibling;
119} RTLOCKVALDDENTRY;
120
121
122/**
123 * Deadlock detection stack.
124 */
125typedef struct RTLOCKVALDDSTACK
126{
127 /** The number stack entries. */
128 uint32_t c;
129 /** The stack entries. */
130 RTLOCKVALDDENTRY a[32];
131} RTLOCKVALDDSTACK;
132/** Pointer to a deadlock detction stack. */
133typedef RTLOCKVALDDSTACK *PRTLOCKVALDDSTACK;
134
135
136/**
137 * Reference to another class.
138 */
139typedef struct RTLOCKVALCLASSREF
140{
141 /** The class. */
142 RTLOCKVALCLASS hClass;
143 /** The number of lookups of this class. */
144 uint32_t volatile cLookups;
145 /** Indicates whether the entry was added automatically during order checking
146 * (true) or manually via the API (false). */
147 bool fAutodidacticism;
148 /** Reserved / explicit alignment padding. */
149 bool afReserved[3];
150} RTLOCKVALCLASSREF;
151/** Pointer to a class reference. */
152typedef RTLOCKVALCLASSREF *PRTLOCKVALCLASSREF;
153
154
155/** Pointer to a chunk of class references. */
156typedef struct RTLOCKVALCLASSREFCHUNK *PRTLOCKVALCLASSREFCHUNK;
157/**
158 * Chunk of class references.
159 */
160typedef struct RTLOCKVALCLASSREFCHUNK
161{
162 /** Array of refs. */
163#if 0 /** @todo for testing alloction of new chunks. */
164 RTLOCKVALCLASSREF aRefs[ARCH_BITS == 32 ? 10 : 8];
165#else
166 RTLOCKVALCLASSREF aRefs[2];
167#endif
168 /** Pointer to the next chunk. */
169 PRTLOCKVALCLASSREFCHUNK volatile pNext;
170} RTLOCKVALCLASSREFCHUNK;
171
172
173/**
174 * Lock class.
175 */
176typedef struct RTLOCKVALCLASSINT
177{
178 /** AVL node core. */
179 AVLLU32NODECORE Core;
180 /** Magic value (RTLOCKVALCLASS_MAGIC). */
181 uint32_t volatile u32Magic;
182 /** Reference counter. See RTLOCKVALCLASS_MAX_REFS. */
183 uint32_t volatile cRefs;
184 /** Whether the class is allowed to teach it self new locking order rules. */
185 bool fAutodidact;
186 /** Whether to allow recursion. */
187 bool fRecursionOk;
188 /** Strict release order. */
189 bool fStrictReleaseOrder;
190 /** Whether this class is in the tree. */
191 bool fInTree;
192 /** Donate a reference to the next retainer. This is a hack to make
193 * RTLockValidatorClassCreateUnique work. */
194 bool volatile fDonateRefToNextRetainer;
195 /** Reserved future use / explicit alignment. */
196 bool afReserved[3];
197 /** The minimum wait interval for which we do deadlock detection
198 * (milliseconds). */
199 RTMSINTERVAL cMsMinDeadlock;
200 /** The minimum wait interval for which we do order checks (milliseconds). */
201 RTMSINTERVAL cMsMinOrder;
202 /** More padding. */
203 uint32_t au32Reserved[ARCH_BITS == 32 ? 5 : 2];
204 /** Classes that may be taken prior to this one.
205 * This is a linked list where each node contains a chunk of locks so that we
206 * reduce the number of allocations as well as localize the data. */
207 RTLOCKVALCLASSREFCHUNK PriorLocks;
208 /** Hash table containing frequently encountered prior locks. */
209 PRTLOCKVALCLASSREF apPriorLocksHash[17];
210 /** Class name. (Allocated after the end of the block as usual.) */
211 char const *pszName;
212 /** Where this class was created.
213 * This is mainly used for finding automatically created lock classes.
214 * @remarks The strings are stored after this structure so we won't crash
215 * if the class lives longer than the module (dll/so/dylib) that
216 * spawned it. */
217 RTLOCKVALSRCPOS CreatePos;
218#ifdef RTLOCKVAL_WITH_CLASS_HASH_STATS
219 /** Hash hits. */
220 uint32_t volatile cHashHits;
221 /** Hash misses. */
222 uint32_t volatile cHashMisses;
223#endif
224} RTLOCKVALCLASSINT;
225AssertCompileSize(AVLLU32NODECORE, ARCH_BITS == 32 ? 20 : 32);
226AssertCompileMemberOffset(RTLOCKVALCLASSINT, PriorLocks, 64);
227
228
229/*******************************************************************************
230* Global Variables *
231*******************************************************************************/
232/** Serializing object destruction and deadlock detection.
233 *
234 * This makes sure that none of the memory examined by the deadlock detection
235 * code will become invalid (reused for other purposes or made not present)
236 * while the detection is in progress.
237 *
238 * NS: RTLOCKVALREC*, RTTHREADINT and RTLOCKVALDRECSHRD::papOwners destruction.
239 * EW: Deadlock detection and some related activities.
240 */
241static RTSEMXROADS g_hLockValidatorXRoads = NIL_RTSEMXROADS;
242/** Whether the lock validator is enabled or disabled.
243 * Only applies to new locks. */
244static bool volatile g_fLockValidatorEnabled = true;
245/** Set if the lock validator is quiet. */
246#ifdef RT_STRICT
247static bool volatile g_fLockValidatorQuiet = false;
248#else
249static bool volatile g_fLockValidatorQuiet = true;
250#endif
251/** Set if the lock validator may panic. */
252#ifdef RT_STRICT
253static bool volatile g_fLockValidatorMayPanic = true;
254#else
255static bool volatile g_fLockValidatorMayPanic = false;
256#endif
257/** Serializing class tree insert and lookups. */
258static RTSEMRW g_hLockValClassTreeRWLock= NIL_RTSEMRW;
259/** Class tree. */
260static PAVLLU32NODECORE g_LockValClassTree = NULL;
261/** Critical section serializing the teaching new rules to the classes. */
262static RTCRITSECT g_LockValClassTeachCS;
263
264
265/*******************************************************************************
266* Internal Functions *
267*******************************************************************************/
268static void rtLockValidatorClassDestroy(RTLOCKVALCLASSINT *pClass);
269static uint32_t rtLockValidatorStackDepth(PRTTHREADINT pThread);
270
271
272/**
273 * Lazy initialization of the lock validator globals.
274 */
275static void rtLockValidatorLazyInit(void)
276{
277 static uint32_t volatile s_fInitializing = false;
278 if (ASMAtomicCmpXchgU32(&s_fInitializing, true, false))
279 {
280 if (!RTCritSectIsInitialized(&g_LockValClassTeachCS))
281 RTCritSectInitEx(&g_LockValClassTeachCS, RTCRITSECT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS,
282 RTLOCKVAL_SUB_CLASS_ANY, "RTLockVal-Teach");
283
284 if (g_hLockValClassTreeRWLock == NIL_RTSEMRW)
285 {
286 RTSEMRW hSemRW;
287 int rc = RTSemRWCreateEx(&hSemRW, RTSEMRW_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, "RTLockVal-Tree");
288 if (RT_SUCCESS(rc))
289 ASMAtomicWriteHandle(&g_hLockValClassTreeRWLock, hSemRW);
290 }
291
292 if (g_hLockValidatorXRoads == NIL_RTSEMXROADS)
293 {
294 RTSEMXROADS hXRoads;
295 int rc = RTSemXRoadsCreate(&hXRoads);
296 if (RT_SUCCESS(rc))
297 ASMAtomicWriteHandle(&g_hLockValidatorXRoads, hXRoads);
298 }
299
300 /** @todo register some cleanup callback if we care. */
301
302 ASMAtomicWriteU32(&s_fInitializing, false);
303 }
304}
305
306
307
308/** Wrapper around ASMAtomicReadPtr. */
309DECL_FORCE_INLINE(PRTLOCKVALRECUNION) rtLockValidatorReadRecUnionPtr(PRTLOCKVALRECUNION volatile *ppRec)
310{
311 PRTLOCKVALRECUNION p = (PRTLOCKVALRECUNION)ASMAtomicReadPtr((void * volatile *)ppRec);
312 RTLOCKVAL_ASSERT_PTR_ALIGN(p);
313 return p;
314}
315
316
317/** Wrapper around ASMAtomicWritePtr. */
318DECL_FORCE_INLINE(void) rtLockValidatorWriteRecUnionPtr(PRTLOCKVALRECUNION volatile *ppRec, PRTLOCKVALRECUNION pRecNew)
319{
320 RTLOCKVAL_ASSERT_PTR_ALIGN(pRecNew);
321 ASMAtomicWritePtr((void * volatile *)ppRec, pRecNew);
322}
323
324
325/** Wrapper around ASMAtomicReadPtr. */
326DECL_FORCE_INLINE(PRTTHREADINT) rtLockValidatorReadThreadHandle(RTTHREAD volatile *phThread)
327{
328 PRTTHREADINT p = (PRTTHREADINT)ASMAtomicReadPtr((void * volatile *)phThread);
329 RTLOCKVAL_ASSERT_PTR_ALIGN(p);
330 return p;
331}
332
333
334/** Wrapper around ASMAtomicUoReadPtr. */
335DECL_FORCE_INLINE(PRTLOCKVALRECSHRDOWN) rtLockValidatorUoReadSharedOwner(PRTLOCKVALRECSHRDOWN volatile *ppOwner)
336{
337 PRTLOCKVALRECSHRDOWN p = (PRTLOCKVALRECSHRDOWN)ASMAtomicUoReadPtr((void * volatile *)ppOwner);
338 RTLOCKVAL_ASSERT_PTR_ALIGN(p);
339 return p;
340}
341
342
343/**
344 * Reads a volatile thread handle field and returns the thread name.
345 *
346 * @returns Thread name (read only).
347 * @param phThread The thread handle field.
348 */
349static const char *rtLockValidatorNameThreadHandle(RTTHREAD volatile *phThread)
350{
351 PRTTHREADINT pThread = rtLockValidatorReadThreadHandle(phThread);
352 if (!pThread)
353 return "<NIL>";
354 if (!VALID_PTR(pThread))
355 return "<INVALID>";
356 if (pThread->u32Magic != RTTHREADINT_MAGIC)
357 return "<BAD-THREAD-MAGIC>";
358 return pThread->szName;
359}
360
361
362/**
363 * Launch a simple assertion like complaint w/ panic.
364 *
365 * @param pszFile Where from - file.
366 * @param iLine Where from - line.
367 * @param pszFunction Where from - function.
368 * @param pszWhat What we're complaining about.
369 * @param ... Format arguments.
370 */
371static void rtLockValComplain(RT_SRC_POS_DECL, const char *pszWhat, ...)
372{
373 if (!ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
374 {
375 RTAssertMsg1Weak("RTLockValidator", iLine, pszFile, pszFunction);
376 va_list va;
377 va_start(va, pszWhat);
378 RTAssertMsg2WeakV(pszWhat, va);
379 va_end(va);
380 }
381 if (!ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
382 RTAssertPanic();
383}
384
385
386/**
387 * Describes the class.
388 *
389 * @param pszPrefix Message prefix.
390 * @param pClass The class to complain about.
391 * @param uSubClass My sub-class.
392 * @param fVerbose Verbose description including relations to other
393 * classes.
394 */
395static void rtLockValComplainAboutClass(const char *pszPrefix, RTLOCKVALCLASSINT *pClass, uint32_t uSubClass, bool fVerbose)
396{
397 if (ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
398 return;
399
400 /* Stringify the sub-class. */
401 const char *pszSubClass;
402 char szSubClass[32];
403 if (uSubClass < RTLOCKVAL_SUB_CLASS_USER)
404 switch (uSubClass)
405 {
406 case RTLOCKVAL_SUB_CLASS_NONE: pszSubClass = "none"; break;
407 case RTLOCKVAL_SUB_CLASS_ANY: pszSubClass = "any"; break;
408 default:
409 RTStrPrintf(szSubClass, sizeof(szSubClass), "invl-%u", uSubClass);
410 pszSubClass = szSubClass;
411 break;
412 }
413 else
414 {
415 RTStrPrintf(szSubClass, sizeof(szSubClass), "%u", uSubClass);
416 pszSubClass = szSubClass;
417 }
418
419 /* Validate the class pointer. */
420 if (!VALID_PTR(pClass))
421 {
422 RTAssertMsg2AddWeak("%sbad class=%p sub-class=%s\n", pszPrefix, pClass, pszSubClass);
423 return;
424 }
425 if (pClass->u32Magic != RTLOCKVALCLASS_MAGIC)
426 {
427 RTAssertMsg2AddWeak("%sbad class=%p magic=%#x sub-class=%s\n", pszPrefix, pClass, pClass->u32Magic, pszSubClass);
428 return;
429 }
430
431 /* OK, dump the class info. */
432 RTAssertMsg2AddWeak("%sclass=%p %s created={%Rbn(%u) %Rfn %p} sub-class=%s\n", pszPrefix,
433 pClass,
434 pClass->pszName,
435 pClass->CreatePos.pszFile,
436 pClass->CreatePos.uLine,
437 pClass->CreatePos.pszFunction,
438 pClass->CreatePos.uId,
439 pszSubClass);
440 if (fVerbose)
441 {
442 uint32_t i = 0;
443 uint32_t cPrinted = 0;
444 for (PRTLOCKVALCLASSREFCHUNK pChunk = &pClass->PriorLocks; pChunk; pChunk = pChunk->pNext)
445 for (unsigned j = 0; j < RT_ELEMENTS(pChunk->aRefs); j++, i++)
446 {
447 RTLOCKVALCLASSINT *pCurClass = pChunk->aRefs[j].hClass;
448 if (pCurClass != NIL_RTLOCKVALCLASS)
449 {
450 RTAssertMsg2AddWeak("%s%s #%02u: %s, %s, %u lookup%s\n", pszPrefix,
451 cPrinted == 0
452 ? "Prior:"
453 : " ",
454 i,
455 pCurClass->pszName,
456 pChunk->aRefs[j].fAutodidacticism
457 ? "autodidactic"
458 : "manually ",
459 pChunk->aRefs[j].cLookups,
460 pChunk->aRefs[j].cLookups != 1 ? "s" : "");
461 cPrinted++;
462 }
463 }
464 if (!cPrinted)
465 RTAssertMsg2AddWeak("%sPrior: none\n", pszPrefix);
466#ifdef RTLOCKVAL_WITH_CLASS_HASH_STATS
467 RTAssertMsg2AddWeak("%sHash Stats: %u hits, %u misses\n", pszPrefix, pClass->cHashHits, pClass->cHashMisses);
468#endif
469 }
470 else
471 {
472 uint32_t cPrinted = 0;
473 for (PRTLOCKVALCLASSREFCHUNK pChunk = &pClass->PriorLocks; pChunk; pChunk = pChunk->pNext)
474 for (unsigned j = 0; j < RT_ELEMENTS(pChunk->aRefs); j++)
475 {
476 RTLOCKVALCLASSINT *pCurClass = pChunk->aRefs[j].hClass;
477 if (pCurClass != NIL_RTLOCKVALCLASS)
478 {
479 if ((cPrinted % 10) == 0)
480 RTAssertMsg2AddWeak("%sPrior classes: %s%s", pszPrefix, pCurClass->pszName,
481 pChunk->aRefs[j].fAutodidacticism ? "*" : "");
482 else if ((cPrinted % 10) != 9)
483 RTAssertMsg2AddWeak(", %s%s", pCurClass->pszName,
484 pChunk->aRefs[j].fAutodidacticism ? "*" : "");
485 else
486 RTAssertMsg2AddWeak(", %s%s\n", pCurClass->pszName,
487 pChunk->aRefs[j].fAutodidacticism ? "*" : "");
488 cPrinted++;
489 }
490 }
491 if (!cPrinted)
492 RTAssertMsg2AddWeak("%sPrior classes: none\n", pszPrefix);
493 else if ((cPrinted % 10) != 0)
494 RTAssertMsg2AddWeak("\n");
495 }
496}
497
498
499/**
500 * Helper for getting the class name.
501 * @returns Class name string.
502 * @param pClass The class.
503 */
504static const char *rtLockValComplainGetClassName(RTLOCKVALCLASSINT *pClass)
505{
506 if (!pClass)
507 return "<nil-class>";
508 if (!VALID_PTR(pClass))
509 return "<bad-class-ptr>";
510 if (pClass->u32Magic != RTLOCKVALCLASS_MAGIC)
511 return "<bad-class-magic>";
512 if (!pClass->pszName)
513 return "<no-class-name>";
514 return pClass->pszName;
515}
516
517/**
518 * Formats the sub-class.
519 *
520 * @returns Stringified sub-class.
521 * @param uSubClass The name.
522 * @param pszBuf Buffer that is big enough.
523 */
524static const char *rtLockValComplainGetSubClassName(uint32_t uSubClass, char *pszBuf)
525{
526 if (uSubClass < RTLOCKVAL_SUB_CLASS_USER)
527 switch (uSubClass)
528 {
529 case RTLOCKVAL_SUB_CLASS_NONE: return "none";
530 case RTLOCKVAL_SUB_CLASS_ANY: return "any";
531 default:
532 RTStrPrintf(pszBuf, 32, "invl-%u", uSubClass);
533 break;
534 }
535 else
536 RTStrPrintf(pszBuf, 32, "%x", uSubClass);
537 return pszBuf;
538}
539
540
541/**
542 * Helper for rtLockValComplainAboutLock.
543 */
544DECL_FORCE_INLINE(void) rtLockValComplainAboutLockHlp(const char *pszPrefix, PRTLOCKVALRECUNION pRec, const char *pszSuffix,
545 uint32_t u32Magic, PCRTLOCKVALSRCPOS pSrcPos, uint32_t cRecursion,
546 const char *pszFrameType)
547{
548 char szBuf[32];
549 switch (u32Magic)
550 {
551 case RTLOCKVALRECEXCL_MAGIC:
552#ifdef RTLOCKVAL_WITH_VERBOSE_DUMPS
553 RTAssertMsg2AddWeak("%s%p %s xrec=%p own=%s r=%u cls=%s/%s pos={%Rbn(%u) %Rfn %p} [x%s]%s", pszPrefix,
554 pRec->Excl.hLock, pRec->Excl.pszName, pRec,
555 rtLockValidatorNameThreadHandle(&pRec->Excl.hThread), cRecursion,
556 rtLockValComplainGetClassName(pRec->Excl.hClass),
557 rtLockValComplainGetSubClassName(pRec->Excl.uSubClass, szBuf),
558 pSrcPos->pszFile, pSrcPos->uLine, pSrcPos->pszFunction, pSrcPos->uId,
559 pszFrameType, pszSuffix);
560#else
561 RTAssertMsg2AddWeak("%s%p %s own=%s r=%u cls=%s/%s pos={%Rbn(%u) %Rfn %p} [x%s]%s", pszPrefix,
562 pRec->Excl.hLock, pRec->Excl.szName,
563 rtLockValidatorNameThreadHandle(&pRec->Excl.hThread), cRecursion,
564 rtLockValComplainGetClassName(pRec->Excl.hClass),
565 rtLockValComplainGetSubClassName(pRec->Excl.uSubClass, szBuf),
566 pSrcPos->pszFile, pSrcPos->uLine, pSrcPos->pszFunction, pSrcPos->uId,
567 pszFrameType, pszSuffix);
568#endif
569 break;
570
571 case RTLOCKVALRECSHRD_MAGIC:
572 RTAssertMsg2AddWeak("%ss %p %s srec=%p cls=%s/%s [s%s]%s", pszPrefix,
573 pRec->Shared.hLock, pRec->Shared.szName, pRec,
574 rtLockValComplainGetClassName(pRec->Shared.hClass),
575 rtLockValComplainGetSubClassName(pRec->Shared.uSubClass, szBuf),
576 pszFrameType, pszSuffix);
577 break;
578
579 case RTLOCKVALRECSHRDOWN_MAGIC:
580 {
581 PRTLOCKVALRECSHRD pShared = pRec->ShrdOwner.pSharedRec;
582 if ( VALID_PTR(pShared)
583 && pShared->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC)
584#ifdef RTLOCKVAL_WITH_VERBOSE_DUMPS
585 RTAssertMsg2AddWeak("%s%p %s srec=%p trec=%p own=%s r=%u cls=%s/%s pos={%Rbn(%u) %Rfn %p} [o%s]%s", pszPrefix,
586 pShared->hLock, pShared->pszName, pShared,
587 pRec, rtLockValidatorNameThreadHandle(&pRec->ShrdOwner.hThread), cRecursion,
588 rtLockValComplainGetClassName(pShared->hClass),
589 rtLockValComplainGetSubClassName(pShared->uSubClass, szBuf),
590 pSrcPos->pszFile, pSrcPos->uLine, pSrcPos->pszFunction, pSrcPos->uId,
591 pszSuffix2, pszSuffix);
592#else
593 RTAssertMsg2AddWeak("%s%p %s own=%s r=%u cls=%s/%s pos={%Rbn(%u) %Rfn %p} [o%s]%s", pszPrefix,
594 pShared->hLock, pShared->szName,
595 rtLockValidatorNameThreadHandle(&pRec->ShrdOwner.hThread), cRecursion,
596 rtLockValComplainGetClassName(pShared->hClass),
597 rtLockValComplainGetSubClassName(pShared->uSubClass, szBuf),
598 pSrcPos->pszFile, pSrcPos->uLine, pSrcPos->pszFunction, pSrcPos->uId,
599 pszFrameType, pszSuffix);
600#endif
601 else
602 RTAssertMsg2AddWeak("%sbad srec=%p trec=%p own=%s r=%u pos={%Rbn(%u) %Rfn %p} [x%s]%s", pszPrefix,
603 pShared,
604 pRec, rtLockValidatorNameThreadHandle(&pRec->ShrdOwner.hThread), cRecursion,
605 pSrcPos->pszFile, pSrcPos->uLine, pSrcPos->pszFunction, pSrcPos->uId,
606 pszFrameType, pszSuffix);
607 break;
608 }
609
610 default:
611 AssertMsgFailed(("%#x\n", u32Magic));
612 }
613}
614
615
616/**
617 * Describes the lock.
618 *
619 * @param pszPrefix Message prefix.
620 * @param pRec The lock record we're working on.
621 * @param pszSuffix Message suffix.
622 */
623static void rtLockValComplainAboutLock(const char *pszPrefix, PRTLOCKVALRECUNION pRec, const char *pszSuffix)
624{
625#ifdef RTLOCKVAL_WITH_RECURSION_RECORDS
626# define FIX_REC(r) 1
627#else
628# define FIX_REC(r) (r)
629#endif
630 if ( VALID_PTR(pRec)
631 && !ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
632 {
633 switch (pRec->Core.u32Magic)
634 {
635 case RTLOCKVALRECEXCL_MAGIC:
636 rtLockValComplainAboutLockHlp(pszPrefix, pRec, pszSuffix, RTLOCKVALRECEXCL_MAGIC,
637 &pRec->Excl.SrcPos, FIX_REC(pRec->Excl.cRecursion), "");
638 break;
639
640 case RTLOCKVALRECSHRD_MAGIC:
641 rtLockValComplainAboutLockHlp(pszPrefix, pRec, pszSuffix, RTLOCKVALRECSHRD_MAGIC, NULL, 0, "");
642 break;
643
644 case RTLOCKVALRECSHRDOWN_MAGIC:
645 rtLockValComplainAboutLockHlp(pszPrefix, pRec, pszSuffix, RTLOCKVALRECSHRDOWN_MAGIC,
646 &pRec->ShrdOwner.SrcPos, FIX_REC(pRec->ShrdOwner.cRecursion), "");
647 break;
648
649 case RTLOCKVALRECNEST_MAGIC:
650 {
651 PRTLOCKVALRECUNION pRealRec = pRec->Nest.pRec;
652 uint32_t u32Magic;
653 if ( VALID_PTR(pRealRec)
654 && ( (u32Magic = pRealRec->Core.u32Magic) == RTLOCKVALRECEXCL_MAGIC
655 || u32Magic == RTLOCKVALRECSHRD_MAGIC
656 || u32Magic == RTLOCKVALRECSHRDOWN_MAGIC)
657 )
658 rtLockValComplainAboutLockHlp(pszPrefix, pRealRec, pszSuffix, u32Magic,
659 &pRec->Nest.SrcPos, pRec->Nest.cRecursion, "/r");
660 else
661 RTAssertMsg2AddWeak("%sbad rrec=%p nrec=%p r=%u pos={%Rbn(%u) %Rfn %p}%s", pszPrefix,
662 pRealRec, pRec, pRec->Nest.cRecursion,
663 pRec->Nest.SrcPos.pszFile, pRec->Nest.SrcPos.uLine, pRec->Nest.SrcPos.pszFunction, pRec->Nest.SrcPos.uId,
664 pszSuffix);
665 break;
666 }
667
668 default:
669 RTAssertMsg2AddWeak("%spRec=%p u32Magic=%#x (bad)%s", pszPrefix, pRec, pRec->Core.u32Magic, pszSuffix);
670 break;
671 }
672 }
673#undef FIX_REC
674}
675
676
677/**
678 * Dump the lock stack.
679 *
680 * @param pThread The thread which lock stack we're gonna dump.
681 * @param cchIndent The indentation in chars.
682 * @param cMinFrames The minimum number of frames to consider
683 * dumping.
684 * @param pHighightRec Record that should be marked specially in the
685 * dump.
686 */
687static void rtLockValComplainAboutLockStack(PRTTHREADINT pThread, unsigned cchIndent, uint32_t cMinFrames,
688 PRTLOCKVALRECUNION pHighightRec)
689{
690 if ( VALID_PTR(pThread)
691 && !ASMAtomicUoReadBool(&g_fLockValidatorQuiet)
692 && pThread->u32Magic == RTTHREADINT_MAGIC
693 )
694 {
695 uint32_t cEntries = rtLockValidatorStackDepth(pThread);
696 if (cEntries >= cMinFrames)
697 {
698 RTAssertMsg2AddWeak("%*s---- start of lock stack for %p %s - %u entr%s ----\n", cchIndent, "",
699 pThread, pThread->szName, cEntries, cEntries == 1 ? "y" : "ies");
700 PRTLOCKVALRECUNION pCur = rtLockValidatorReadRecUnionPtr(&pThread->LockValidator.pStackTop);
701 for (uint32_t i = 0; VALID_PTR(pCur); i++)
702 {
703 char szPrefix[80];
704 RTStrPrintf(szPrefix, sizeof(szPrefix), "%*s#%02u: ", cchIndent, "", i);
705 rtLockValComplainAboutLock(szPrefix, pCur, pHighightRec != pCur ? "\n" : " (*)\n");
706 switch (pCur->Core.u32Magic)
707 {
708 case RTLOCKVALRECEXCL_MAGIC: pCur = rtLockValidatorReadRecUnionPtr(&pCur->Excl.pDown); break;
709 case RTLOCKVALRECSHRDOWN_MAGIC: pCur = rtLockValidatorReadRecUnionPtr(&pCur->ShrdOwner.pDown); break;
710 case RTLOCKVALRECNEST_MAGIC: pCur = rtLockValidatorReadRecUnionPtr(&pCur->Nest.pDown); break;
711 default:
712 RTAssertMsg2AddWeak("%*s<bad stack frame>\n", cchIndent, "");
713 pCur = NULL;
714 break;
715 }
716 }
717 RTAssertMsg2AddWeak("%*s---- end of lock stack ----\n", cchIndent, "");
718 }
719 }
720}
721
722
723/**
724 * Launch the initial complaint.
725 *
726 * @param pszWhat What we're complaining about.
727 * @param pSrcPos Where we are complaining from, as it were.
728 * @param pThreadSelf The calling thread.
729 * @param pRec The main lock involved. Can be NULL.
730 * @param fDumpStack Whether to dump the lock stack (true) or not
731 * (false).
732 */
733static void rtLockValComplainFirst(const char *pszWhat, PCRTLOCKVALSRCPOS pSrcPos, PRTTHREADINT pThreadSelf,
734 PRTLOCKVALRECUNION pRec, bool fDumpStack)
735{
736 if (!ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
737 {
738 ASMCompilerBarrier(); /* paranoia */
739 RTAssertMsg1Weak("RTLockValidator", pSrcPos ? pSrcPos->uLine : 0, pSrcPos ? pSrcPos->pszFile : NULL, pSrcPos ? pSrcPos->pszFunction : NULL);
740 if (pSrcPos && pSrcPos->uId)
741 RTAssertMsg2Weak("%s [uId=%p thrd=%s]\n", pszWhat, pSrcPos->uId, VALID_PTR(pThreadSelf) ? pThreadSelf->szName : "<NIL>");
742 else
743 RTAssertMsg2Weak("%s [thrd=%s]\n", pszWhat, VALID_PTR(pThreadSelf) ? pThreadSelf->szName : "<NIL>");
744 rtLockValComplainAboutLock("Lock: ", pRec, "\n");
745 if (fDumpStack)
746 rtLockValComplainAboutLockStack(pThreadSelf, 0, 1, pRec);
747 }
748}
749
750
751/**
752 * Continue bitching.
753 *
754 * @param pszFormat Format string.
755 * @param ... Format arguments.
756 */
757static void rtLockValComplainMore(const char *pszFormat, ...)
758{
759 if (!ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
760 {
761 va_list va;
762 va_start(va, pszFormat);
763 RTAssertMsg2AddWeakV(pszFormat, va);
764 va_end(va);
765 }
766}
767
768
769/**
770 * Raise a panic if enabled.
771 */
772static void rtLockValComplainPanic(void)
773{
774 if (ASMAtomicUoReadBool(&g_fLockValidatorMayPanic))
775 RTAssertPanic();
776}
777
778
779/**
780 * Copy a source position record.
781 *
782 * @param pDst The destination.
783 * @param pSrc The source. Can be NULL.
784 */
785DECL_FORCE_INLINE(void) rtLockValidatorSrcPosCopy(PRTLOCKVALSRCPOS pDst, PCRTLOCKVALSRCPOS pSrc)
786{
787 if (pSrc)
788 {
789 ASMAtomicUoWriteU32(&pDst->uLine, pSrc->uLine);
790 ASMAtomicUoWritePtr((void * volatile *)&pDst->pszFile, pSrc->pszFile);
791 ASMAtomicUoWritePtr((void * volatile *)&pDst->pszFunction, pSrc->pszFunction);
792 ASMAtomicUoWritePtr((void * volatile *)&pDst->uId, (void *)pSrc->uId);
793 }
794 else
795 {
796 ASMAtomicUoWriteU32(&pDst->uLine, 0);
797 ASMAtomicUoWritePtr((void * volatile *)&pDst->pszFile, NULL);
798 ASMAtomicUoWritePtr((void * volatile *)&pDst->pszFunction, NULL);
799 ASMAtomicUoWritePtr((void * volatile *)&pDst->uId, 0);
800 }
801}
802
803
804/**
805 * Init a source position record.
806 *
807 * @param pSrcPos The source position record.
808 */
809DECL_FORCE_INLINE(void) rtLockValidatorSrcPosInit(PRTLOCKVALSRCPOS pSrcPos)
810{
811 pSrcPos->pszFile = NULL;
812 pSrcPos->pszFunction = NULL;
813 pSrcPos->uId = 0;
814 pSrcPos->uLine = 0;
815#if HC_ARCH_BITS == 64
816 pSrcPos->u32Padding = 0;
817#endif
818}
819
820
821/* sdbm:
822 This algorithm was created for sdbm (a public-domain reimplementation of
823 ndbm) database library. it was found to do well in scrambling bits,
824 causing better distribution of the keys and fewer splits. it also happens
825 to be a good general hashing function with good distribution. the actual
826 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
827 is the faster version used in gawk. [there is even a faster, duff-device
828 version] the magic constant 65599 was picked out of thin air while
829 experimenting with different constants, and turns out to be a prime.
830 this is one of the algorithms used in berkeley db (see sleepycat) and
831 elsewhere. */
832DECL_FORCE_INLINE(uint32_t) sdbm(const char *str, uint32_t hash)
833{
834 uint8_t *pu8 = (uint8_t *)str;
835 int c;
836
837 while ((c = *pu8++))
838 hash = c + (hash << 6) + (hash << 16) - hash;
839
840 return hash;
841}
842
843
844/**
845 * Hashes the specified source position.
846 *
847 * @returns Hash.
848 * @param pSrcPos The source position record.
849 */
850static uint32_t rtLockValidatorSrcPosHash(PCRTLOCKVALSRCPOS pSrcPos)
851{
852 uint32_t uHash;
853 if ( ( pSrcPos->pszFile
854 || pSrcPos->pszFunction)
855 && pSrcPos->uLine != 0)
856 {
857 uHash = 0;
858 if (pSrcPos->pszFile)
859 uHash = sdbm(pSrcPos->pszFile, uHash);
860 if (pSrcPos->pszFunction)
861 uHash = sdbm(pSrcPos->pszFunction, uHash);
862 uHash += pSrcPos->uLine;
863 }
864 else
865 {
866 Assert(pSrcPos->uId);
867 uHash = (uint32_t)pSrcPos->uId;
868 }
869
870 return uHash;
871}
872
873
874/**
875 * Compares two source positions.
876 *
877 * @returns 0 if equal, < 0 if pSrcPos1 is smaller than pSrcPos2, > 0 if
878 * otherwise.
879 * @param pSrcPos1 The first source position.
880 * @param pSrcPos2 The second source position.
881 */
882static int rtLockValidatorSrcPosCompare(PCRTLOCKVALSRCPOS pSrcPos1, PCRTLOCKVALSRCPOS pSrcPos2)
883{
884 if (pSrcPos1->uLine != pSrcPos2->uLine)
885 return pSrcPos1->uLine < pSrcPos2->uLine ? -1 : 1;
886
887 int iDiff = RTStrCmp(pSrcPos1->pszFile, pSrcPos2->pszFile);
888 if (iDiff != 0)
889 return iDiff;
890
891 iDiff = RTStrCmp(pSrcPos1->pszFunction, pSrcPos2->pszFunction);
892 if (iDiff != 0)
893 return iDiff;
894
895 if (pSrcPos1->uId != pSrcPos2->uId)
896 return pSrcPos1->uId < pSrcPos2->uId ? -1 : 1;
897 return 0;
898}
899
900
901
902/**
903 * Serializes destruction of RTLOCKVALREC* and RTTHREADINT structures.
904 */
905DECLHIDDEN(void) rtLockValidatorSerializeDestructEnter(void)
906{
907 RTSEMXROADS hXRoads = g_hLockValidatorXRoads;
908 if (hXRoads != NIL_RTSEMXROADS)
909 RTSemXRoadsNSEnter(hXRoads);
910}
911
912
913/**
914 * Call after rtLockValidatorSerializeDestructEnter.
915 */
916DECLHIDDEN(void) rtLockValidatorSerializeDestructLeave(void)
917{
918 RTSEMXROADS hXRoads = g_hLockValidatorXRoads;
919 if (hXRoads != NIL_RTSEMXROADS)
920 RTSemXRoadsNSLeave(hXRoads);
921}
922
923
924/**
925 * Serializes deadlock detection against destruction of the objects being
926 * inspected.
927 */
928DECLINLINE(void) rtLockValidatorSerializeDetectionEnter(void)
929{
930 RTSEMXROADS hXRoads = g_hLockValidatorXRoads;
931 if (hXRoads != NIL_RTSEMXROADS)
932 RTSemXRoadsEWEnter(hXRoads);
933}
934
935
936/**
937 * Call after rtLockValidatorSerializeDetectionEnter.
938 */
939DECLHIDDEN(void) rtLockValidatorSerializeDetectionLeave(void)
940{
941 RTSEMXROADS hXRoads = g_hLockValidatorXRoads;
942 if (hXRoads != NIL_RTSEMXROADS)
943 RTSemXRoadsEWLeave(hXRoads);
944}
945
946
947/**
948 * Initializes the per thread lock validator data.
949 *
950 * @param pPerThread The data.
951 */
952DECLHIDDEN(void) rtLockValidatorInitPerThread(RTLOCKVALPERTHREAD *pPerThread)
953{
954 pPerThread->bmFreeShrdOwners = UINT32_MAX;
955
956 /* ASSUMES the rest has already been zeroed. */
957 Assert(pPerThread->pRec == NULL);
958 Assert(pPerThread->cWriteLocks == 0);
959 Assert(pPerThread->cReadLocks == 0);
960 Assert(pPerThread->fInValidator == false);
961 Assert(pPerThread->pStackTop == NULL);
962}
963
964
965/**
966 * Delete the per thread lock validator data.
967 *
968 * @param pPerThread The data.
969 */
970DECLHIDDEN(void) rtLockValidatorDeletePerThread(RTLOCKVALPERTHREAD *pPerThread)
971{
972 /*
973 * Check that the thread doesn't own any locks at this time.
974 */
975 if (pPerThread->pStackTop)
976 {
977 rtLockValComplainFirst("Thread terminating owning locks!", NULL,
978 RT_FROM_MEMBER(pPerThread, RTTHREADINT, LockValidator),
979 pPerThread->pStackTop, true);
980 rtLockValComplainPanic();
981 }
982
983 /*
984 * Free the recursion records.
985 */
986 PRTLOCKVALRECNEST pCur = pPerThread->pFreeNestRecs;
987 pPerThread->pFreeNestRecs = NULL;
988 while (pCur)
989 {
990 PRTLOCKVALRECNEST pNext = pCur->pNextFree;
991 RTMemFree(pNext);
992 pCur = pNext;
993 }
994}
995
996RTDECL(int) RTLockValidatorClassCreateEx(PRTLOCKVALCLASS phClass, PCRTLOCKVALSRCPOS pSrcPos,
997 bool fAutodidact, bool fRecursionOk, bool fStrictReleaseOrder,
998 RTMSINTERVAL cMsMinDeadlock, RTMSINTERVAL cMsMinOrder,
999 const char *pszNameFmt, ...)
1000{
1001 va_list va;
1002 va_start(va, pszNameFmt);
1003 int rc = RTLockValidatorClassCreateExV(phClass, pSrcPos, fAutodidact, fRecursionOk, fStrictReleaseOrder,
1004 cMsMinDeadlock, cMsMinOrder, pszNameFmt, va);
1005 va_end(va);
1006 return rc;
1007}
1008
1009
1010RTDECL(int) RTLockValidatorClassCreateExV(PRTLOCKVALCLASS phClass, PCRTLOCKVALSRCPOS pSrcPos,
1011 bool fAutodidact, bool fRecursionOk, bool fStrictReleaseOrder,
1012 RTMSINTERVAL cMsMinDeadlock, RTMSINTERVAL cMsMinOrder,
1013 const char *pszNameFmt, va_list va)
1014{
1015 Assert(cMsMinDeadlock >= 1);
1016 Assert(cMsMinOrder >= 1);
1017 AssertPtr(pSrcPos);
1018
1019 /*
1020 * Format the name and calc its length.
1021 */
1022 size_t cbName;
1023 char szName[32];
1024 if (pszNameFmt && *pszNameFmt)
1025 cbName = RTStrPrintfV(szName, sizeof(szName), pszNameFmt, va) + 1;
1026 else
1027 {
1028 static uint32_t volatile s_cAnonymous = 0;
1029 uint32_t i = ASMAtomicIncU32(&s_cAnonymous);
1030 cbName = RTStrPrintf(szName, sizeof(szName), "anon-%u", i - 1) + 1;
1031 }
1032
1033 /*
1034 * Figure out the file and function name lengths and allocate memory for
1035 * it all.
1036 */
1037 size_t const cbFile = pSrcPos->pszFile ? strlen(pSrcPos->pszFile) + 1 : 0;
1038 size_t const cbFunction = pSrcPos->pszFile ? strlen(pSrcPos->pszFunction) + 1 : 0;
1039 RTLOCKVALCLASSINT *pThis = (RTLOCKVALCLASSINT *)RTMemAlloc(sizeof(*pThis) + cbFile + cbFunction + cbName);
1040 if (!pThis)
1041 return VERR_NO_MEMORY;
1042
1043 /*
1044 * Initialize the class data.
1045 */
1046 pThis->Core.Key = rtLockValidatorSrcPosHash(pSrcPos);
1047 pThis->Core.uchHeight = 0;
1048 pThis->Core.pLeft = NULL;
1049 pThis->Core.pRight = NULL;
1050 pThis->Core.pList = NULL;
1051 pThis->u32Magic = RTLOCKVALCLASS_MAGIC;
1052 pThis->cRefs = 1;
1053 pThis->fAutodidact = fAutodidact;
1054 pThis->fRecursionOk = fRecursionOk;
1055 pThis->fStrictReleaseOrder = fStrictReleaseOrder;
1056 pThis->fInTree = false;
1057 pThis->fDonateRefToNextRetainer = false;
1058 pThis->afReserved[0] = false;
1059 pThis->afReserved[1] = false;
1060 pThis->afReserved[2] = false;
1061 pThis->cMsMinDeadlock = cMsMinDeadlock;
1062 pThis->cMsMinOrder = cMsMinOrder;
1063 for (unsigned i = 0; i < RT_ELEMENTS(pThis->au32Reserved); i++)
1064 pThis->au32Reserved[i] = 0;
1065 for (unsigned i = 0; i < RT_ELEMENTS(pThis->au32Reserved); i++)
1066 {
1067 pThis->PriorLocks.aRefs[i].hClass = NIL_RTLOCKVALCLASS;
1068 pThis->PriorLocks.aRefs[i].cLookups = 0;
1069 pThis->PriorLocks.aRefs[i].fAutodidacticism = false;
1070 pThis->PriorLocks.aRefs[i].afReserved[0] = false;
1071 pThis->PriorLocks.aRefs[i].afReserved[1] = false;
1072 pThis->PriorLocks.aRefs[i].afReserved[2] = false;
1073 }
1074 pThis->PriorLocks.pNext = NULL;
1075 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apPriorLocksHash); i++)
1076 pThis->apPriorLocksHash[i] = NULL;
1077 char *pszDst = (char *)(pThis + 1);
1078 pThis->pszName = (char *)memcpy(pszDst, szName, cbName);
1079 pszDst += cbName;
1080 rtLockValidatorSrcPosCopy(&pThis->CreatePos, pSrcPos);
1081 pThis->CreatePos.pszFile = pSrcPos->pszFile ? (char *)memcpy(pszDst, pSrcPos->pszFile, cbFile) : NULL;
1082 pszDst += cbFile;
1083 pThis->CreatePos.pszFunction= pSrcPos->pszFunction ? (char *)memcpy(pszDst, pSrcPos->pszFunction, cbFunction) : NULL;
1084 Assert(rtLockValidatorSrcPosHash(&pThis->CreatePos) == pThis->Core.Key);
1085#ifdef RTLOCKVAL_WITH_CLASS_HASH_STATS
1086 pThis->cHashHits = 0;
1087 pThis->cHashMisses = 0;
1088#endif
1089
1090 *phClass = pThis;
1091 return VINF_SUCCESS;
1092}
1093
1094
1095RTDECL(int) RTLockValidatorClassCreate(PRTLOCKVALCLASS phClass, bool fAutodidact, RT_SRC_POS_DECL, const char *pszNameFmt, ...)
1096{
1097 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_POS_NO_ID();
1098 va_list va;
1099 va_start(va, pszNameFmt);
1100 int rc = RTLockValidatorClassCreateExV(phClass, &SrcPos,
1101 fAutodidact, true /*fRecursionOk*/, false /*fStrictReleaseOrder*/,
1102 1 /*cMsMinDeadlock*/, 1 /*cMsMinOrder*/,
1103 pszNameFmt, va);
1104 va_end(va);
1105 return rc;
1106}
1107
1108
1109/**
1110 * Creates a new lock validator class with a reference that is consumed by the
1111 * first call to RTLockValidatorClassRetain.
1112 *
1113 * This is tailored for use in the parameter list of a semaphore constructor.
1114 *
1115 * @returns Class handle with a reference that is automatically consumed by the
1116 * first retainer. NIL_RTLOCKVALCLASS if we run into trouble.
1117 *
1118 * @param pszFile The source position of the call, file.
1119 * @param iLine The source position of the call, line.
1120 * @param pszFunction The source position of the call, function.
1121 * @param pszNameFmt Class name format string, optional (NULL). Max
1122 * length is 32 bytes.
1123 * @param ... Format string arguments.
1124 */
1125RTDECL(RTLOCKVALCLASS) RTLockValidatorClassCreateUnique(RT_SRC_POS_DECL, const char *pszNameFmt, ...)
1126{
1127 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_POS_NO_ID();
1128 RTLOCKVALCLASSINT *pClass;
1129 va_list va;
1130 va_start(va, pszNameFmt);
1131 int rc = RTLockValidatorClassCreateExV(&pClass, &SrcPos,
1132 true /*fAutodidact*/, true /*fRecursionOk*/, false /*fStrictReleaseOrder*/,
1133 1 /*cMsMinDeadlock*/, 1 /*cMsMinOrder*/,
1134 pszNameFmt, va);
1135 va_end(va);
1136 if (RT_FAILURE(rc))
1137 return NIL_RTLOCKVALCLASS;
1138 ASMAtomicWriteBool(&pClass->fDonateRefToNextRetainer, true); /* see rtLockValidatorClassRetain */
1139 return pClass;
1140}
1141
1142
1143/**
1144 * Internal class retainer.
1145 * @returns The new reference count.
1146 * @param pClass The class.
1147 */
1148DECL_FORCE_INLINE(uint32_t) rtLockValidatorClassRetain(RTLOCKVALCLASSINT *pClass)
1149{
1150 uint32_t cRefs = ASMAtomicIncU32(&pClass->cRefs);
1151 if (cRefs > RTLOCKVALCLASS_MAX_REFS)
1152 ASMAtomicWriteU32(&pClass->cRefs, RTLOCKVALCLASS_MAX_REFS);
1153 else if ( cRefs == 2
1154 && ASMAtomicXchgBool(&pClass->fDonateRefToNextRetainer, false))
1155 cRefs = ASMAtomicDecU32(&pClass->cRefs);
1156 return cRefs;
1157}
1158
1159
1160/**
1161 * Validates and retains a lock validator class.
1162 *
1163 * @returns @a hClass on success, NIL_RTLOCKVALCLASS on failure.
1164 * @param hClass The class handle. NIL_RTLOCKVALCLASS is ok.
1165 */
1166DECL_FORCE_INLINE(RTLOCKVALCLASS) rtLockValidatorClassValidateAndRetain(RTLOCKVALCLASS hClass)
1167{
1168 if (hClass == NIL_RTLOCKVALCLASS)
1169 return hClass;
1170 AssertPtrReturn(hClass, NIL_RTLOCKVALCLASS);
1171 AssertReturn(hClass->u32Magic == RTLOCKVALCLASS_MAGIC, NIL_RTLOCKVALCLASS);
1172 rtLockValidatorClassRetain(hClass);
1173 return hClass;
1174}
1175
1176
1177/**
1178 * Internal class releaser.
1179 * @returns The new reference count.
1180 * @param pClass The class.
1181 */
1182DECLINLINE(uint32_t) rtLockValidatorClassRelease(RTLOCKVALCLASSINT *pClass)
1183{
1184 uint32_t cRefs = ASMAtomicDecU32(&pClass->cRefs);
1185 if (cRefs + 1 == RTLOCKVALCLASS_MAX_REFS)
1186 ASMAtomicWriteU32(&pClass->cRefs, RTLOCKVALCLASS_MAX_REFS);
1187 else if (!cRefs)
1188 rtLockValidatorClassDestroy(pClass);
1189 return cRefs;
1190}
1191
1192
1193/**
1194 * Destroys a class once there are not more references to it.
1195 *
1196 * @param Class The class.
1197 */
1198static void rtLockValidatorClassDestroy(RTLOCKVALCLASSINT *pClass)
1199{
1200 AssertReturnVoid(!pClass->fInTree);
1201 ASMAtomicWriteU32(&pClass->u32Magic, RTLOCKVALCLASS_MAGIC_DEAD);
1202
1203 PRTLOCKVALCLASSREFCHUNK pChunk = &pClass->PriorLocks;
1204 while (pChunk)
1205 {
1206 for (uint32_t i = 0; i < RT_ELEMENTS(pChunk->aRefs); i++)
1207 {
1208 RTLOCKVALCLASSINT *pClass2 = pChunk->aRefs[i].hClass;
1209 if (pClass2 != NIL_RTLOCKVALCLASS)
1210 {
1211 pChunk->aRefs[i].hClass = NIL_RTLOCKVALCLASS;
1212 rtLockValidatorClassRelease(pClass2);
1213 }
1214 }
1215
1216 PRTLOCKVALCLASSREFCHUNK pNext = pChunk->pNext;
1217 pChunk->pNext = NULL;
1218 if (pChunk != &pClass->PriorLocks)
1219 RTMemFree(pChunk);
1220 pChunk = pNext;
1221 }
1222
1223 RTMemFree(pClass);
1224}
1225
1226
1227RTDECL(RTLOCKVALCLASS) RTLockValidatorClassFindForSrcPos(PRTLOCKVALSRCPOS pSrcPos)
1228{
1229 if (g_hLockValClassTreeRWLock == NIL_RTSEMRW)
1230 rtLockValidatorLazyInit();
1231 int rcLock = RTSemRWRequestRead(g_hLockValClassTreeRWLock, RT_INDEFINITE_WAIT);
1232
1233 uint32_t uSrcPosHash = rtLockValidatorSrcPosHash(pSrcPos);
1234 RTLOCKVALCLASSINT *pClass = (RTLOCKVALCLASSINT *)RTAvllU32Get(&g_LockValClassTree, uSrcPosHash);
1235 while (pClass)
1236 {
1237 if (rtLockValidatorSrcPosCompare(&pClass->CreatePos, pSrcPos) == 0)
1238 break;
1239 pClass = (RTLOCKVALCLASSINT *)pClass->Core.pList;
1240 }
1241
1242 if (RT_SUCCESS(rcLock))
1243 RTSemRWReleaseRead(g_hLockValClassTreeRWLock);
1244 return pClass;
1245}
1246
1247
1248RTDECL(RTLOCKVALCLASS) RTLockValidatorClassForSrcPos(RT_SRC_POS_DECL, const char *pszNameFmt, ...)
1249{
1250 RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_POS_NO_ID();
1251 RTLOCKVALCLASS hClass = RTLockValidatorClassFindForSrcPos(&SrcPos);
1252 if (hClass == NIL_RTLOCKVALCLASS)
1253 {
1254 /*
1255 * Create a new class and insert it into the tree.
1256 */
1257 va_list va;
1258 va_start(va, pszNameFmt);
1259 int rc = RTLockValidatorClassCreateExV(&hClass, &SrcPos,
1260 true /*fAutodidact*/, true /*fRecursionOk*/, false /*fStrictReleaseOrder*/,
1261 1 /*cMsMinDeadlock*/, 1 /*cMsMinOrder*/,
1262 pszNameFmt, va);
1263 va_end(va);
1264 if (RT_SUCCESS(rc))
1265 {
1266 if (g_hLockValClassTreeRWLock == NIL_RTSEMRW)
1267 rtLockValidatorLazyInit();
1268 int rcLock = RTSemRWRequestWrite(g_hLockValClassTreeRWLock, RT_INDEFINITE_WAIT);
1269
1270 Assert(!hClass->fInTree);
1271 hClass->fInTree = RTAvllU32Insert(&g_LockValClassTree, &hClass->Core);
1272 Assert(hClass->fInTree);
1273
1274 if (RT_SUCCESS(rcLock))
1275 RTSemRWReleaseWrite(g_hLockValClassTreeRWLock);
1276 return hClass;
1277 }
1278 }
1279 return hClass;
1280}
1281
1282
1283RTDECL(uint32_t) RTLockValidatorClassRetain(RTLOCKVALCLASS hClass)
1284{
1285 RTLOCKVALCLASSINT *pClass = hClass;
1286 AssertPtrReturn(pClass, UINT32_MAX);
1287 AssertReturn(pClass->u32Magic == RTLOCKVALCLASS_MAGIC, UINT32_MAX);
1288 return rtLockValidatorClassRetain(pClass);
1289}
1290
1291
1292RTDECL(uint32_t) RTLockValidatorClassRelease(RTLOCKVALCLASS hClass)
1293{
1294 RTLOCKVALCLASSINT *pClass = hClass;
1295 if (pClass == NIL_RTLOCKVALCLASS)
1296 return 0;
1297 AssertPtrReturn(pClass, UINT32_MAX);
1298 AssertReturn(pClass->u32Magic == RTLOCKVALCLASS_MAGIC, UINT32_MAX);
1299 return rtLockValidatorClassRelease(pClass);
1300}
1301
1302
1303/**
1304 * Worker for rtLockValidatorClassIsPriorClass that does a linear search thru
1305 * all the chunks for @a pPriorClass.
1306 *
1307 * @returns true / false.
1308 * @param pClass The class to search.
1309 * @param pPriorClass The class to search for.
1310 */
1311static bool rtLockValidatorClassIsPriorClassByLinearSearch(RTLOCKVALCLASSINT *pClass, RTLOCKVALCLASSINT *pPriorClass)
1312{
1313 for (PRTLOCKVALCLASSREFCHUNK pChunk = &pClass->PriorLocks; pChunk; pChunk = pChunk->pNext)
1314 for (uint32_t i = 0; i < RT_ELEMENTS(pChunk->aRefs); i++)
1315 {
1316 if (pChunk->aRefs[i].hClass == pPriorClass)
1317 {
1318 uint32_t cLookups = ASMAtomicIncU32(&pChunk->aRefs[i].cLookups);
1319 if (RT_UNLIKELY(cLookups >= RTLOCKVALCLASSREF_MAX_LOOKUPS_FIX))
1320 {
1321 ASMAtomicWriteU32(&pChunk->aRefs[i].cLookups, RTLOCKVALCLASSREF_MAX_LOOKUPS);
1322 cLookups = RTLOCKVALCLASSREF_MAX_LOOKUPS;
1323 }
1324
1325 /* update the hash table entry. */
1326 PRTLOCKVALCLASSREF *ppHashEntry = &pClass->apPriorLocksHash[RTLOCKVALCLASS_HASH(pPriorClass)];
1327 if ( !(*ppHashEntry)
1328 || (*ppHashEntry)->cLookups + 128 < cLookups)
1329 ASMAtomicWritePtr((void * volatile *)ppHashEntry, &pChunk->aRefs[i]);
1330
1331#ifdef RTLOCKVAL_WITH_CLASS_HASH_STATS
1332 ASMAtomicIncU32(&pClass->cHashMisses);
1333#endif
1334 return true;
1335 }
1336 }
1337
1338 return false;
1339}
1340
1341
1342/**
1343 * Checks if @a pPriorClass is a known prior class.
1344 *
1345 * @returns true / false.
1346 * @param pClass The class to search.
1347 * @param pPriorClass The class to search for.
1348 */
1349DECL_FORCE_INLINE(bool) rtLockValidatorClassIsPriorClass(RTLOCKVALCLASSINT *pClass, RTLOCKVALCLASSINT *pPriorClass)
1350{
1351 /*
1352 * Hash lookup here.
1353 */
1354 PRTLOCKVALCLASSREF pRef = pClass->apPriorLocksHash[RTLOCKVALCLASS_HASH(pPriorClass)];
1355 if ( pRef
1356 && pRef->hClass == pPriorClass)
1357 {
1358 uint32_t cLookups = ASMAtomicIncU32(&pRef->cLookups);
1359 if (RT_UNLIKELY(cLookups >= RTLOCKVALCLASSREF_MAX_LOOKUPS_FIX))
1360 ASMAtomicWriteU32(&pRef->cLookups, RTLOCKVALCLASSREF_MAX_LOOKUPS);
1361#ifdef RTLOCKVAL_WITH_CLASS_HASH_STATS
1362 ASMAtomicIncU32(&pClass->cHashHits);
1363#endif
1364 return true;
1365 }
1366
1367 return rtLockValidatorClassIsPriorClassByLinearSearch(pClass, pPriorClass);
1368}
1369
1370
1371/**
1372 * Adds a class to the prior list.
1373 *
1374 * @returns VINF_SUCCESS, VERR_NO_MEMORY or VERR_SEM_LV_WRONG_ORDER.
1375 * @param pClass The class to work on.
1376 * @param pPriorClass The class to add.
1377 * @param fAutodidacticism Whether we're teaching ourselfs (true) or
1378 * somebody is teaching us via the API (false).
1379 * @param pSrcPos Where this rule was added (optional).
1380 */
1381static int rtLockValidatorClassAddPriorClass(RTLOCKVALCLASSINT *pClass, RTLOCKVALCLASSINT *pPriorClass,
1382 bool fAutodidacticism, PCRTLOCKVALSRCPOS pSrcPos)
1383{
1384 if (!RTCritSectIsInitialized(&g_LockValClassTeachCS))
1385 rtLockValidatorLazyInit();
1386 int rcLock = RTCritSectEnter(&g_LockValClassTeachCS);
1387
1388 /*
1389 * Check that there are no conflict (no assert since we might race each other).
1390 */
1391 int rc = VERR_SEM_LV_INTERNAL_ERROR;
1392 if (!rtLockValidatorClassIsPriorClass(pPriorClass, pClass))
1393 {
1394 if (!rtLockValidatorClassIsPriorClass(pClass, pPriorClass))
1395 {
1396 /*
1397 * Scan the table for a free entry, allocating a new chunk if necessary.
1398 */
1399 for (PRTLOCKVALCLASSREFCHUNK pChunk = &pClass->PriorLocks; ; pChunk = pChunk->pNext)
1400 {
1401 bool fDone = false;
1402 for (uint32_t i = 0; i < RT_ELEMENTS(pChunk->aRefs); i++)
1403 {
1404 ASMAtomicCmpXchgHandle(&pChunk->aRefs[i].hClass, pPriorClass, NIL_RTLOCKVALCLASS, fDone);
1405 if (fDone)
1406 {
1407 pChunk->aRefs[i].fAutodidacticism = fAutodidacticism;
1408 rtLockValidatorClassRetain(pPriorClass);
1409 rc = VINF_SUCCESS;
1410 break;
1411 }
1412 }
1413 if (fDone)
1414 break;
1415
1416 /* If no more chunks, allocate a new one and insert the class before linking it. */
1417 if (!pChunk->pNext)
1418 {
1419 PRTLOCKVALCLASSREFCHUNK pNew = (PRTLOCKVALCLASSREFCHUNK)RTMemAlloc(sizeof(*pNew));
1420 if (!pNew)
1421 {
1422 rc = VERR_NO_MEMORY;
1423 break;
1424 }
1425 pNew->pNext = NULL;
1426 for (uint32_t i = 0; i < RT_ELEMENTS(pNew->aRefs); i++)
1427 {
1428 pNew->aRefs[i].hClass = NIL_RTLOCKVALCLASS;
1429 pNew->aRefs[i].cLookups = 0;
1430 pNew->aRefs[i].fAutodidacticism = false;
1431 pNew->aRefs[i].afReserved[0] = false;
1432 pNew->aRefs[i].afReserved[1] = false;
1433 pNew->aRefs[i].afReserved[2] = false;
1434 }
1435
1436 pNew->aRefs[0].hClass = pPriorClass;
1437 pNew->aRefs[0].fAutodidacticism = fAutodidacticism;
1438
1439 ASMAtomicWritePtr((void * volatile *)&pChunk->pNext, pNew);
1440 rtLockValidatorClassRetain(pPriorClass);
1441 rc = VINF_SUCCESS;
1442 break;
1443 }
1444 } /* chunk loop */
1445 }
1446 else
1447 rc = VINF_SUCCESS;
1448 }
1449 else
1450 rc = VERR_SEM_LV_WRONG_ORDER;
1451
1452 if (RT_SUCCESS(rcLock))
1453 RTCritSectLeave(&g_LockValClassTeachCS);
1454 return rc;
1455}
1456
1457
1458RTDECL(int) RTLockValidatorClassAddPriorClass(RTLOCKVALCLASS hClass, RTLOCKVALCLASS hPriorClass)
1459{
1460 RTLOCKVALCLASSINT *pClass = hClass;
1461 AssertPtrReturn(pClass, VERR_INVALID_HANDLE);
1462 AssertReturn(pClass->u32Magic == RTLOCKVALCLASS_MAGIC, VERR_INVALID_HANDLE);
1463
1464 RTLOCKVALCLASSINT *pPriorClass = hPriorClass;
1465 AssertPtrReturn(pPriorClass, VERR_INVALID_HANDLE);
1466 AssertReturn(pPriorClass->u32Magic == RTLOCKVALCLASS_MAGIC, VERR_INVALID_HANDLE);
1467
1468 return rtLockValidatorClassAddPriorClass(pClass, pPriorClass, false /*fAutodidacticism*/, NULL);
1469}
1470
1471
1472RTDECL(int) RTLockValidatorClassEnforceStrictReleaseOrder(RTLOCKVALCLASS hClass, bool fEnabled)
1473{
1474 RTLOCKVALCLASSINT *pClass = hClass;
1475 AssertPtrReturn(pClass, VERR_INVALID_HANDLE);
1476 AssertReturn(pClass->u32Magic == RTLOCKVALCLASS_MAGIC, VERR_INVALID_HANDLE);
1477
1478 ASMAtomicWriteBool(&pClass->fStrictReleaseOrder, fEnabled);
1479 return VINF_SUCCESS;
1480}
1481
1482
1483/**
1484 * Unlinks all siblings.
1485 *
1486 * This is used during record deletion and assumes no races.
1487 *
1488 * @param pCore One of the siblings.
1489 */
1490static void rtLockValidatorUnlinkAllSiblings(PRTLOCKVALRECCORE pCore)
1491{
1492 /* ASSUMES sibling destruction doesn't involve any races and that all
1493 related records are to be disposed off now. */
1494 PRTLOCKVALRECUNION pSibling = (PRTLOCKVALRECUNION)pCore;
1495 while (pSibling)
1496 {
1497 PRTLOCKVALRECUNION volatile *ppCoreNext;
1498 switch (pSibling->Core.u32Magic)
1499 {
1500 case RTLOCKVALRECEXCL_MAGIC:
1501 case RTLOCKVALRECEXCL_MAGIC_DEAD:
1502 ppCoreNext = &pSibling->Excl.pSibling;
1503 break;
1504
1505 case RTLOCKVALRECSHRD_MAGIC:
1506 case RTLOCKVALRECSHRD_MAGIC_DEAD:
1507 ppCoreNext = &pSibling->Shared.pSibling;
1508 break;
1509
1510 default:
1511 AssertFailed();
1512 ppCoreNext = NULL;
1513 break;
1514 }
1515 if (RT_UNLIKELY(ppCoreNext))
1516 break;
1517 pSibling = (PRTLOCKVALRECUNION)ASMAtomicXchgPtr((void * volatile *)ppCoreNext, NULL);
1518 }
1519}
1520
1521
1522RTDECL(int) RTLockValidatorRecMakeSiblings(PRTLOCKVALRECCORE pRec1, PRTLOCKVALRECCORE pRec2)
1523{
1524 /*
1525 * Validate input.
1526 */
1527 PRTLOCKVALRECUNION p1 = (PRTLOCKVALRECUNION)pRec1;
1528 PRTLOCKVALRECUNION p2 = (PRTLOCKVALRECUNION)pRec2;
1529
1530 AssertPtrReturn(p1, VERR_SEM_LV_INVALID_PARAMETER);
1531 AssertReturn( p1->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC
1532 || p1->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
1533 , VERR_SEM_LV_INVALID_PARAMETER);
1534
1535 AssertPtrReturn(p2, VERR_SEM_LV_INVALID_PARAMETER);
1536 AssertReturn( p2->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC
1537 || p2->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
1538 , VERR_SEM_LV_INVALID_PARAMETER);
1539
1540 /*
1541 * Link them (circular list).
1542 */
1543 if ( p1->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC
1544 && p2->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC)
1545 {
1546 p1->Excl.pSibling = p2;
1547 p2->Shared.pSibling = p1;
1548 }
1549 else if ( p1->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
1550 && p2->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC)
1551 {
1552 p1->Shared.pSibling = p2;
1553 p2->Excl.pSibling = p1;
1554 }
1555 else
1556 AssertFailedReturn(VERR_SEM_LV_INVALID_PARAMETER); /* unsupported mix */
1557
1558 return VINF_SUCCESS;
1559}
1560
1561
1562/**
1563 * Gets the lock name for the given record.
1564 *
1565 * @returns Read-only lock name.
1566 * @param pRec The lock record.
1567 */
1568DECL_FORCE_INLINE(const char *) rtLockValidatorRecName(PRTLOCKVALRECUNION pRec)
1569{
1570 switch (pRec->Core.u32Magic)
1571 {
1572 case RTLOCKVALRECEXCL_MAGIC:
1573 return pRec->Excl.szName;
1574 case RTLOCKVALRECSHRD_MAGIC:
1575 return pRec->Shared.szName;
1576 case RTLOCKVALRECSHRDOWN_MAGIC:
1577 return pRec->ShrdOwner.pSharedRec ? pRec->ShrdOwner.pSharedRec->szName : "orphaned";
1578 case RTLOCKVALRECNEST_MAGIC:
1579 pRec = rtLockValidatorReadRecUnionPtr(&pRec->Nest.pRec);
1580 if (VALID_PTR(pRec))
1581 {
1582 switch (pRec->Core.u32Magic)
1583 {
1584 case RTLOCKVALRECEXCL_MAGIC:
1585 return pRec->Excl.szName;
1586 case RTLOCKVALRECSHRD_MAGIC:
1587 return pRec->Shared.szName;
1588 case RTLOCKVALRECSHRDOWN_MAGIC:
1589 return pRec->ShrdOwner.pSharedRec ? pRec->ShrdOwner.pSharedRec->szName : "orphaned";
1590 default:
1591 return "unknown-nested";
1592 }
1593 }
1594 return "orphaned-nested";
1595 default:
1596 return "unknown";
1597 }
1598}
1599
1600
1601/**
1602 * Gets the class for this locking record.
1603 *
1604 * @returns Pointer to the class or NIL_RTLOCKVALCLASS.
1605 * @param pRec The lock validator record.
1606 */
1607DECLINLINE(RTLOCKVALCLASSINT *) rtLockValidatorRecGetClass(PRTLOCKVALRECUNION pRec)
1608{
1609 switch (pRec->Core.u32Magic)
1610 {
1611 case RTLOCKVALRECEXCL_MAGIC:
1612 return pRec->Excl.hClass;
1613
1614 case RTLOCKVALRECSHRD_MAGIC:
1615 return pRec->Shared.hClass;
1616
1617 case RTLOCKVALRECSHRDOWN_MAGIC:
1618 {
1619 PRTLOCKVALRECSHRD pSharedRec = pRec->ShrdOwner.pSharedRec;
1620 if (RT_LIKELY( VALID_PTR(pSharedRec)
1621 && pSharedRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC))
1622 return pSharedRec->hClass;
1623 return NIL_RTLOCKVALCLASS;
1624 }
1625
1626 case RTLOCKVALRECNEST_MAGIC:
1627 {
1628 PRTLOCKVALRECUNION pRealRec = pRec->Nest.pRec;
1629 if (VALID_PTR(pRealRec))
1630 {
1631 switch (pRealRec->Core.u32Magic)
1632 {
1633 case RTLOCKVALRECEXCL_MAGIC:
1634 return pRealRec->Excl.hClass;
1635
1636 case RTLOCKVALRECSHRDOWN_MAGIC:
1637 {
1638 PRTLOCKVALRECSHRD pSharedRec = pRealRec->ShrdOwner.pSharedRec;
1639 if (RT_LIKELY( VALID_PTR(pSharedRec)
1640 && pSharedRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC))
1641 return pSharedRec->hClass;
1642 break;
1643 }
1644
1645 default:
1646 AssertMsgFailed(("%p %p %#x\n", pRec, pRealRec, pRealRec->Core.u32Magic));
1647 break;
1648 }
1649 }
1650 return NIL_RTLOCKVALCLASS;
1651 }
1652
1653 default:
1654 AssertMsgFailed(("%#x\n", pRec->Core.u32Magic));
1655 return NIL_RTLOCKVALCLASS;
1656 }
1657}
1658
1659
1660/**
1661 * Gets the class for this locking record and the pointer to the one below it in
1662 * the stack.
1663 *
1664 * @returns Pointer to the class or NIL_RTLOCKVALCLASS.
1665 * @param pRec The lock validator record.
1666 * @param puSubClass Where to return the sub-class.
1667 * @param ppDown Where to return the pointer to the record below.
1668 */
1669DECL_FORCE_INLINE(RTLOCKVALCLASSINT *)
1670rtLockValidatorRecGetClassesAndDown(PRTLOCKVALRECUNION pRec, uint32_t *puSubClass, PRTLOCKVALRECUNION *ppDown)
1671{
1672 switch (pRec->Core.u32Magic)
1673 {
1674 case RTLOCKVALRECEXCL_MAGIC:
1675 *ppDown = pRec->Excl.pDown;
1676 *puSubClass = pRec->Excl.uSubClass;
1677 return pRec->Excl.hClass;
1678
1679 case RTLOCKVALRECSHRD_MAGIC:
1680 *ppDown = NULL;
1681 *puSubClass = pRec->Shared.uSubClass;
1682 return pRec->Shared.hClass;
1683
1684 case RTLOCKVALRECSHRDOWN_MAGIC:
1685 {
1686 *ppDown = pRec->ShrdOwner.pDown;
1687
1688 PRTLOCKVALRECSHRD pSharedRec = pRec->ShrdOwner.pSharedRec;
1689 if (RT_LIKELY( VALID_PTR(pSharedRec)
1690 && pSharedRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC))
1691 {
1692 *puSubClass = pSharedRec->uSubClass;
1693 return pSharedRec->hClass;
1694 }
1695 *puSubClass = RTLOCKVAL_SUB_CLASS_NONE;
1696 return NIL_RTLOCKVALCLASS;
1697 }
1698
1699 case RTLOCKVALRECNEST_MAGIC:
1700 {
1701 *ppDown = pRec->Nest.pDown;
1702
1703 PRTLOCKVALRECUNION pRealRec = pRec->Nest.pRec;
1704 if (VALID_PTR(pRealRec))
1705 {
1706 switch (pRealRec->Core.u32Magic)
1707 {
1708 case RTLOCKVALRECEXCL_MAGIC:
1709 *puSubClass = pRealRec->Excl.uSubClass;
1710 return pRealRec->Excl.hClass;
1711
1712 case RTLOCKVALRECSHRDOWN_MAGIC:
1713 {
1714 PRTLOCKVALRECSHRD pSharedRec = pRealRec->ShrdOwner.pSharedRec;
1715 if (RT_LIKELY( VALID_PTR(pSharedRec)
1716 && pSharedRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC))
1717 {
1718 *puSubClass = pSharedRec->uSubClass;
1719 return pSharedRec->hClass;
1720 }
1721 break;
1722 }
1723
1724 default:
1725 AssertMsgFailed(("%p %p %#x\n", pRec, pRealRec, pRealRec->Core.u32Magic));
1726 break;
1727 }
1728 }
1729 *puSubClass = RTLOCKVAL_SUB_CLASS_NONE;
1730 return NIL_RTLOCKVALCLASS;
1731 }
1732
1733 default:
1734 AssertMsgFailed(("%#x\n", pRec->Core.u32Magic));
1735 *ppDown = NULL;
1736 *puSubClass = RTLOCKVAL_SUB_CLASS_NONE;
1737 return NIL_RTLOCKVALCLASS;
1738 }
1739}
1740
1741
1742/**
1743 * Gets the sub-class for a lock record.
1744 *
1745 * @returns the sub-class.
1746 * @param pRec The lock validator record.
1747 */
1748DECLINLINE(uint32_t) rtLockValidatorRecGetSubClass(PRTLOCKVALRECUNION pRec)
1749{
1750 switch (pRec->Core.u32Magic)
1751 {
1752 case RTLOCKVALRECEXCL_MAGIC:
1753 return pRec->Excl.uSubClass;
1754
1755 case RTLOCKVALRECSHRD_MAGIC:
1756 return pRec->Shared.uSubClass;
1757
1758 case RTLOCKVALRECSHRDOWN_MAGIC:
1759 {
1760 PRTLOCKVALRECSHRD pSharedRec = pRec->ShrdOwner.pSharedRec;
1761 if (RT_LIKELY( VALID_PTR(pSharedRec)
1762 && pSharedRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC))
1763 return pSharedRec->uSubClass;
1764 return RTLOCKVAL_SUB_CLASS_NONE;
1765 }
1766
1767 case RTLOCKVALRECNEST_MAGIC:
1768 {
1769 PRTLOCKVALRECUNION pRealRec = pRec->Nest.pRec;
1770 if (VALID_PTR(pRealRec))
1771 {
1772 switch (pRealRec->Core.u32Magic)
1773 {
1774 case RTLOCKVALRECEXCL_MAGIC:
1775 return pRec->Excl.uSubClass;
1776
1777 case RTLOCKVALRECSHRDOWN_MAGIC:
1778 {
1779 PRTLOCKVALRECSHRD pSharedRec = pRealRec->ShrdOwner.pSharedRec;
1780 if (RT_LIKELY( VALID_PTR(pSharedRec)
1781 && pSharedRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC))
1782 return pSharedRec->uSubClass;
1783 break;
1784 }
1785
1786 default:
1787 AssertMsgFailed(("%p %p %#x\n", pRec, pRealRec, pRealRec->Core.u32Magic));
1788 break;
1789 }
1790 }
1791 return RTLOCKVAL_SUB_CLASS_NONE;
1792 }
1793
1794 default:
1795 AssertMsgFailed(("%#x\n", pRec->Core.u32Magic));
1796 return RTLOCKVAL_SUB_CLASS_NONE;
1797 }
1798}
1799
1800
1801
1802
1803/**
1804 * Calculates the depth of a lock stack.
1805 *
1806 * @returns Number of stack frames.
1807 * @param pThread The thread.
1808 */
1809static uint32_t rtLockValidatorStackDepth(PRTTHREADINT pThread)
1810{
1811 uint32_t cEntries = 0;
1812 PRTLOCKVALRECUNION pCur = rtLockValidatorReadRecUnionPtr(&pThread->LockValidator.pStackTop);
1813 while (VALID_PTR(pCur))
1814 {
1815 switch (pCur->Core.u32Magic)
1816 {
1817 case RTLOCKVALRECEXCL_MAGIC:
1818 pCur = rtLockValidatorReadRecUnionPtr(&pCur->Excl.pDown);
1819 break;
1820
1821 case RTLOCKVALRECSHRDOWN_MAGIC:
1822 pCur = rtLockValidatorReadRecUnionPtr(&pCur->ShrdOwner.pDown);
1823 break;
1824
1825 case RTLOCKVALRECNEST_MAGIC:
1826 pCur = rtLockValidatorReadRecUnionPtr(&pCur->Nest.pDown);
1827 break;
1828
1829 default:
1830 AssertMsgFailedReturn(("%#x\n", pCur->Core.u32Magic), cEntries);
1831 }
1832 cEntries++;
1833 }
1834 return cEntries;
1835}
1836
1837
1838/**
1839 * Checks if the stack contains @a pRec.
1840 *
1841 * @returns true / false.
1842 * @param pThreadSelf The curren thread.
1843 * @param pRec The lock record.
1844 */
1845static bool rtLockValidatorStackContainsRec(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec)
1846{
1847 PRTLOCKVALRECUNION pCur = pThreadSelf->LockValidator.pStackTop;
1848 while (pCur)
1849 {
1850 AssertPtrReturn(pCur, false);
1851 if (pCur == pRec)
1852 return true;
1853 switch (pCur->Core.u32Magic)
1854 {
1855 case RTLOCKVALRECEXCL_MAGIC:
1856 Assert(pCur->Excl.cRecursion >= 1);
1857 pCur = pCur->Excl.pDown;
1858 break;
1859
1860 case RTLOCKVALRECSHRDOWN_MAGIC:
1861 Assert(pCur->ShrdOwner.cRecursion >= 1);
1862 pCur = pCur->ShrdOwner.pDown;
1863 break;
1864
1865 case RTLOCKVALRECNEST_MAGIC:
1866 Assert(pCur->Nest.cRecursion > 1);
1867 pCur = pCur->Nest.pDown;
1868 break;
1869
1870 default:
1871 AssertMsgFailedReturn(("%#x\n", pCur->Core.u32Magic), false);
1872 }
1873 }
1874 return false;
1875}
1876
1877
1878/**
1879 * Pushes a lock record onto the stack.
1880 *
1881 * @param pThreadSelf The current thread.
1882 * @param pRec The lock record.
1883 */
1884static void rtLockValidatorStackPush(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec)
1885{
1886 Assert(pThreadSelf == RTThreadSelf());
1887 Assert(!rtLockValidatorStackContainsRec(pThreadSelf, pRec));
1888
1889 switch (pRec->Core.u32Magic)
1890 {
1891 case RTLOCKVALRECEXCL_MAGIC:
1892 Assert(pRec->Excl.cRecursion == 1);
1893 Assert(pRec->Excl.pDown == NULL);
1894 rtLockValidatorWriteRecUnionPtr(&pRec->Excl.pDown, pThreadSelf->LockValidator.pStackTop);
1895 break;
1896
1897 case RTLOCKVALRECSHRDOWN_MAGIC:
1898 Assert(pRec->ShrdOwner.cRecursion == 1);
1899 Assert(pRec->ShrdOwner.pDown == NULL);
1900 rtLockValidatorWriteRecUnionPtr(&pRec->ShrdOwner.pDown, pThreadSelf->LockValidator.pStackTop);
1901 break;
1902
1903 default:
1904 AssertMsgFailedReturnVoid(("%#x\n", pRec->Core.u32Magic));
1905 }
1906 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pStackTop, pRec);
1907}
1908
1909
1910/**
1911 * Pops a lock record off the stack.
1912 *
1913 * @param pThreadSelf The current thread.
1914 * @param pRec The lock.
1915 */
1916static void rtLockValidatorStackPop(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec)
1917{
1918 Assert(pThreadSelf == RTThreadSelf());
1919
1920 PRTLOCKVALRECUNION pDown;
1921 switch (pRec->Core.u32Magic)
1922 {
1923 case RTLOCKVALRECEXCL_MAGIC:
1924 Assert(pRec->Excl.cRecursion == 0);
1925 pDown = pRec->Excl.pDown;
1926 rtLockValidatorWriteRecUnionPtr(&pRec->Excl.pDown, NULL); /* lazy bird */
1927 break;
1928
1929 case RTLOCKVALRECSHRDOWN_MAGIC:
1930 Assert(pRec->ShrdOwner.cRecursion == 0);
1931 pDown = pRec->ShrdOwner.pDown;
1932 rtLockValidatorWriteRecUnionPtr(&pRec->ShrdOwner.pDown, NULL);
1933 break;
1934
1935 default:
1936 AssertMsgFailedReturnVoid(("%#x\n", pRec->Core.u32Magic));
1937 }
1938 if (pThreadSelf->LockValidator.pStackTop == pRec)
1939 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pStackTop, pDown);
1940 else
1941 {
1942 /* Find the pointer to our record and unlink ourselves. */
1943 PRTLOCKVALRECUNION pCur = pThreadSelf->LockValidator.pStackTop;
1944 while (pCur)
1945 {
1946 PRTLOCKVALRECUNION volatile *ppDown;
1947 switch (pCur->Core.u32Magic)
1948 {
1949 case RTLOCKVALRECEXCL_MAGIC:
1950 Assert(pCur->Excl.cRecursion >= 1);
1951 ppDown = &pCur->Excl.pDown;
1952 break;
1953
1954 case RTLOCKVALRECSHRDOWN_MAGIC:
1955 Assert(pCur->ShrdOwner.cRecursion >= 1);
1956 ppDown = &pCur->ShrdOwner.pDown;
1957 break;
1958
1959 case RTLOCKVALRECNEST_MAGIC:
1960 Assert(pCur->Nest.cRecursion >= 1);
1961 ppDown = &pCur->Nest.pDown;
1962 break;
1963
1964 default:
1965 AssertMsgFailedReturnVoid(("%#x\n", pCur->Core.u32Magic));
1966 }
1967 pCur = *ppDown;
1968 if (pCur == pRec)
1969 {
1970 rtLockValidatorWriteRecUnionPtr(ppDown, pDown);
1971 return;
1972 }
1973 }
1974 AssertMsgFailed(("%p %p\n", pRec, pThreadSelf));
1975 }
1976}
1977
1978
1979/**
1980 * Creates and pushes lock recursion record onto the stack.
1981 *
1982 * @param pThreadSelf The current thread.
1983 * @param pRec The lock record.
1984 * @param pSrcPos Where the recursion occured.
1985 */
1986static void rtLockValidatorStackPushRecursion(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec, PCRTLOCKVALSRCPOS pSrcPos)
1987{
1988 Assert(pThreadSelf == RTThreadSelf());
1989 Assert(rtLockValidatorStackContainsRec(pThreadSelf, pRec));
1990
1991#ifdef RTLOCKVAL_WITH_RECURSION_RECORDS
1992 /*
1993 * Allocate a new recursion record
1994 */
1995 PRTLOCKVALRECNEST pRecursionRec = pThreadSelf->LockValidator.pFreeNestRecs;
1996 if (pRecursionRec)
1997 pThreadSelf->LockValidator.pFreeNestRecs = pRecursionRec->pNextFree;
1998 else
1999 {
2000 pRecursionRec = (PRTLOCKVALRECNEST)RTMemAlloc(sizeof(*pRecursionRec));
2001 if (!pRecursionRec)
2002 return;
2003 }
2004
2005 /*
2006 * Initialize it.
2007 */
2008 switch (pRec->Core.u32Magic)
2009 {
2010 case RTLOCKVALRECEXCL_MAGIC:
2011 pRecursionRec->cRecursion = pRec->Excl.cRecursion;
2012 break;
2013
2014 case RTLOCKVALRECSHRDOWN_MAGIC:
2015 pRecursionRec->cRecursion = pRec->ShrdOwner.cRecursion;
2016 break;
2017
2018 default:
2019 AssertMsgFailed(("%#x\n", pRec->Core.u32Magic));
2020 rtLockValidatorSerializeDestructEnter();
2021 rtLockValidatorSerializeDestructLeave();
2022 RTMemFree(pRecursionRec);
2023 return;
2024 }
2025 Assert(pRecursionRec->cRecursion > 1);
2026 pRecursionRec->pRec = pRec;
2027 pRecursionRec->pDown = NULL;
2028 pRecursionRec->pNextFree = NULL;
2029 rtLockValidatorSrcPosCopy(&pRecursionRec->SrcPos, pSrcPos);
2030 pRecursionRec->Core.u32Magic = RTLOCKVALRECNEST_MAGIC;
2031
2032 /*
2033 * Link it.
2034 */
2035 pRecursionRec->pDown = pThreadSelf->LockValidator.pStackTop;
2036 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pStackTop, (PRTLOCKVALRECUNION)pRecursionRec);
2037#endif /* RTLOCKVAL_WITH_RECURSION_RECORDS */
2038}
2039
2040
2041/**
2042 * Pops a lock recursion record off the stack.
2043 *
2044 * @param pThreadSelf The current thread.
2045 * @param pRec The lock record.
2046 */
2047static void rtLockValidatorStackPopRecursion(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec)
2048{
2049 Assert(pThreadSelf == RTThreadSelf());
2050 Assert(rtLockValidatorStackContainsRec(pThreadSelf, pRec));
2051
2052 uint32_t cRecursion;
2053 switch (pRec->Core.u32Magic)
2054 {
2055 case RTLOCKVALRECEXCL_MAGIC: cRecursion = pRec->Excl.cRecursion; break;
2056 case RTLOCKVALRECSHRDOWN_MAGIC: cRecursion = pRec->ShrdOwner.cRecursion; break;
2057 default: AssertMsgFailedReturnVoid(("%#x\n", pRec->Core.u32Magic));
2058 }
2059 Assert(cRecursion >= 1);
2060
2061#ifdef RTLOCKVAL_WITH_RECURSION_RECORDS
2062 /*
2063 * Pop the recursion record.
2064 */
2065 PRTLOCKVALRECUNION pNest = pThreadSelf->LockValidator.pStackTop;
2066 if ( pNest != NULL
2067 && pNest->Core.u32Magic == RTLOCKVALRECNEST_MAGIC
2068 && pNest->Nest.pRec == pRec
2069 )
2070 {
2071 Assert(pNest->Nest.cRecursion == cRecursion + 1);
2072 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pStackTop, pNest->Nest.pDown);
2073 }
2074 else
2075 {
2076 /* Find the record above ours. */
2077 PRTLOCKVALRECUNION volatile *ppDown = NULL;
2078 for (;;)
2079 {
2080 AssertMsgReturnVoid(pNest, ("%p %p\n", pRec, pThreadSelf));
2081 switch (pNest->Core.u32Magic)
2082 {
2083 case RTLOCKVALRECEXCL_MAGIC:
2084 ppDown = &pNest->Excl.pDown;
2085 pNest = *ppDown;
2086 continue;
2087 case RTLOCKVALRECSHRDOWN_MAGIC:
2088 ppDown = &pNest->ShrdOwner.pDown;
2089 pNest = *ppDown;
2090 continue;
2091 case RTLOCKVALRECNEST_MAGIC:
2092 if (pNest->Nest.pRec == pRec)
2093 break;
2094 ppDown = &pNest->Nest.pDown;
2095 pNest = *ppDown;
2096 continue;
2097 default:
2098 AssertMsgFailedReturnVoid(("%#x\n", pNest->Core.u32Magic));
2099 }
2100 break; /* ugly */
2101 }
2102 Assert(pNest->Nest.cRecursion == cRecursion + 1);
2103 rtLockValidatorWriteRecUnionPtr(ppDown, pNest->Nest.pDown);
2104 }
2105
2106 /*
2107 * Invalidate and free the record.
2108 */
2109 ASMAtomicWriteU32(&pNest->Core.u32Magic, RTLOCKVALRECNEST_MAGIC);
2110 rtLockValidatorWriteRecUnionPtr(&pNest->Nest.pDown, NULL);
2111 rtLockValidatorWriteRecUnionPtr(&pNest->Nest.pRec, NULL);
2112 pNest->Nest.cRecursion = 0;
2113 pNest->Nest.pNextFree = pThreadSelf->LockValidator.pFreeNestRecs;
2114 pThreadSelf->LockValidator.pFreeNestRecs = &pNest->Nest;
2115#endif /* RTLOCKVAL_WITH_RECURSION_RECORDS */
2116}
2117
2118
2119/**
2120 * Helper for rtLockValidatorStackCheckLockingOrder that does the bitching and
2121 * returns VERR_SEM_LV_WRONG_ORDER.
2122 */
2123static int rtLockValidatorStackWrongOrder(const char *pszWhat, PCRTLOCKVALSRCPOS pSrcPos, PRTTHREADINT pThreadSelf,
2124 PRTLOCKVALRECUNION pRec1, PRTLOCKVALRECUNION pRec2,
2125 RTLOCKVALCLASSINT *pClass1, RTLOCKVALCLASSINT *pClass2)
2126
2127
2128{
2129 rtLockValComplainFirst(pszWhat, pSrcPos, pThreadSelf, pRec1, false);
2130 rtLockValComplainAboutLock("Other lock: ", pRec2, "\n");
2131 rtLockValComplainAboutClass("My class: ", pClass1, rtLockValidatorRecGetSubClass(pRec1), true /*fVerbose*/);
2132 rtLockValComplainAboutClass("Other class: ", pClass2, rtLockValidatorRecGetSubClass(pRec2), true /*fVerbose*/);
2133 rtLockValComplainAboutLockStack(pThreadSelf, 0, 0, pRec2);
2134 rtLockValComplainPanic();
2135 return VERR_SEM_LV_WRONG_ORDER;
2136}
2137
2138
2139/**
2140 * Checks if the sub-class order is ok or not.
2141 *
2142 * Used to deal with two locks from the same class.
2143 *
2144 * @returns true if ok, false if not.
2145 * @param uSubClass1 The sub-class of the lock that is being
2146 * considered.
2147 * @param uSubClass2 The sub-class of the lock that is already being
2148 * held.
2149 */
2150DECL_FORCE_INLINE(bool) rtLockValidatorIsSubClassOrderOk(uint32_t uSubClass1, uint32_t uSubClass2)
2151{
2152 if (uSubClass1 > uSubClass2)
2153 {
2154 /* NONE kills ANY. */
2155 if (uSubClass2 == RTLOCKVAL_SUB_CLASS_NONE)
2156 return false;
2157 return true;
2158 }
2159
2160 /* ANY counters all USER values. (uSubClass1 == NONE only if they are equal) */
2161 AssertCompile(RTLOCKVAL_SUB_CLASS_ANY > RTLOCKVAL_SUB_CLASS_NONE);
2162 if (uSubClass1 == RTLOCKVAL_SUB_CLASS_ANY)
2163 return true;
2164 return false;
2165}
2166
2167
2168/**
2169 * Checks if the class and sub-class lock order is ok.
2170 *
2171 * @returns true if ok, false if not.
2172 * @param pClass1 The class of the lock that is being considered.
2173 * @param uSubClass1 The sub-class that goes with @a pClass1.
2174 * @param pClass2 The class of the lock that is already being
2175 * held.
2176 * @param uSubClass2 The sub-class that goes with @a pClass2.
2177 */
2178DECL_FORCE_INLINE(bool) rtLockValidatorIsClassOrderOk(RTLOCKVALCLASSINT *pClass1, uint32_t uSubClass1,
2179 RTLOCKVALCLASSINT *pClass2, uint32_t uSubClass2)
2180{
2181 if (pClass1 == pClass2)
2182 return rtLockValidatorIsSubClassOrderOk(uSubClass1, uSubClass2);
2183 return rtLockValidatorClassIsPriorClass(pClass1, pClass2);
2184}
2185
2186
2187/**
2188 * Checks the locking order, part two.
2189 *
2190 * @returns VINF_SUCCESS, VERR_SEM_LV_WRONG_ORDER or VERR_SEM_LV_INTERNAL_ERROR.
2191 * @param pClass The lock class.
2192 * @param uSubClass The lock sub-class.
2193 * @param pThreadSelf The current thread.
2194 * @param pRec The lock record.
2195 * @param pSrcPos The source position of the locking operation.
2196 */
2197static int rtLockValidatorStackCheckLockingOrder2(RTLOCKVALCLASSINT * const pClass, uint32_t const uSubClass,
2198 PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION const pRec,
2199 PCRTLOCKVALSRCPOS const pSrcPos,
2200 RTLOCKVALCLASSINT * const pFirstBadClass,
2201 PRTLOCKVALRECUNION const pFirstBadRec,
2202 PRTLOCKVALRECUNION const pFirstBadDown)
2203{
2204 /*
2205 * Something went wrong, pCur is pointing to where.
2206 */
2207 if ( pClass == pFirstBadClass
2208 || rtLockValidatorClassIsPriorClass(pFirstBadClass, pClass))
2209 return rtLockValidatorStackWrongOrder("Wrong locking order!", pSrcPos, pThreadSelf,
2210 pRec, pFirstBadRec, pClass, pFirstBadClass);
2211 if (!pClass->fAutodidact)
2212 return rtLockValidatorStackWrongOrder("Wrong locking order! (unknown)", pSrcPos, pThreadSelf,
2213 pRec, pFirstBadRec, pClass, pFirstBadClass);
2214
2215 /*
2216 * This class is an autodidact, so we have to check out the rest of the stack
2217 * for direct violations.
2218 */
2219 uint32_t cNewRules = 1;
2220 PRTLOCKVALRECUNION pCur = pFirstBadDown;
2221 while (pCur)
2222 {
2223 AssertPtrReturn(pCur, VERR_SEM_LV_INTERNAL_ERROR);
2224
2225 if (pCur->Core.u32Magic == RTLOCKVALRECNEST_MAGIC)
2226 pCur = pCur->Nest.pDown;
2227 else
2228 {
2229 PRTLOCKVALRECUNION pDown;
2230 uint32_t uPriorSubClass;
2231 RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
2232 if (pPriorClass != NIL_RTLOCKVALCLASS)
2233 {
2234 AssertPtrReturn(pPriorClass, VERR_SEM_LV_INTERNAL_ERROR);
2235 AssertReturn(pPriorClass->u32Magic == RTLOCKVALCLASS_MAGIC, VERR_SEM_LV_INTERNAL_ERROR);
2236 if (!rtLockValidatorIsClassOrderOk(pClass, uSubClass, pPriorClass, uPriorSubClass))
2237 {
2238 if ( pClass == pPriorClass
2239 || rtLockValidatorClassIsPriorClass(pPriorClass, pClass))
2240 return rtLockValidatorStackWrongOrder("Wrong locking order! (more than one)", pSrcPos, pThreadSelf,
2241 pRec, pCur, pClass, pPriorClass);
2242 cNewRules++;
2243 }
2244 }
2245 pCur = pDown;
2246 }
2247 }
2248
2249 if (cNewRules == 1)
2250 {
2251 /*
2252 * Special case the simple operation, hoping that it will be a
2253 * frequent case.
2254 */
2255 int rc = rtLockValidatorClassAddPriorClass(pClass, pFirstBadClass, true /*fAutodidacticism*/, pSrcPos);
2256 if (rc == VERR_SEM_LV_WRONG_ORDER)
2257 return rtLockValidatorStackWrongOrder("Wrong locking order! (race)", pSrcPos, pThreadSelf,
2258 pRec, pFirstBadRec, pClass, pFirstBadClass);
2259 Assert(RT_SUCCESS(rc) || rc == VERR_NO_MEMORY);
2260 }
2261 else
2262 {
2263 /*
2264 * We may be adding more than one rule, so we have to take the lock
2265 * before starting to add the rules. This means we have to check
2266 * the state after taking it since we might be racing someone adding
2267 * a conflicting rule.
2268 */
2269 if (!RTCritSectIsInitialized(&g_LockValClassTeachCS))
2270 rtLockValidatorLazyInit();
2271 int rcLock = RTCritSectEnter(&g_LockValClassTeachCS);
2272
2273 /* Check */
2274 pCur = pFirstBadRec;
2275 while (pCur)
2276 {
2277 if (pCur->Core.u32Magic == RTLOCKVALRECNEST_MAGIC)
2278 pCur = pCur->Nest.pDown;
2279 else
2280 {
2281 uint32_t uPriorSubClass;
2282 PRTLOCKVALRECUNION pDown;
2283 RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
2284 if (pPriorClass != NIL_RTLOCKVALCLASS)
2285 {
2286 if (!rtLockValidatorIsClassOrderOk(pClass, uSubClass, pPriorClass, uPriorSubClass))
2287 {
2288 if ( pClass == pPriorClass
2289 || rtLockValidatorClassIsPriorClass(pPriorClass, pClass))
2290 {
2291 if (RT_SUCCESS(rcLock))
2292 RTCritSectLeave(&g_LockValClassTeachCS);
2293 return rtLockValidatorStackWrongOrder("Wrong locking order! (2nd)", pSrcPos, pThreadSelf,
2294 pRec, pCur, pClass, pPriorClass);
2295 }
2296 }
2297 }
2298 pCur = pDown;
2299 }
2300 }
2301
2302 /* Iterate the stack yet again, adding new rules this time. */
2303 pCur = pFirstBadRec;
2304 while (pCur)
2305 {
2306 if (pCur->Core.u32Magic == RTLOCKVALRECNEST_MAGIC)
2307 pCur = pCur->Nest.pDown;
2308 else
2309 {
2310 uint32_t uPriorSubClass;
2311 PRTLOCKVALRECUNION pDown;
2312 RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
2313 if (pPriorClass != NIL_RTLOCKVALCLASS)
2314 {
2315 if (!rtLockValidatorIsClassOrderOk(pClass, uSubClass, pPriorClass, uPriorSubClass))
2316 {
2317 Assert( pClass != pPriorClass
2318 && !rtLockValidatorClassIsPriorClass(pPriorClass, pClass));
2319 int rc = rtLockValidatorClassAddPriorClass(pClass, pPriorClass, true /*fAutodidacticism*/, pSrcPos);
2320 if (RT_FAILURE(rc))
2321 {
2322 Assert(rc == VERR_NO_MEMORY);
2323 break;
2324 }
2325 Assert(rtLockValidatorClassIsPriorClass(pClass, pPriorClass));
2326 }
2327 }
2328 pCur = pDown;
2329 }
2330 }
2331
2332 if (RT_SUCCESS(rcLock))
2333 RTCritSectLeave(&g_LockValClassTeachCS);
2334 }
2335
2336 return VINF_SUCCESS;
2337}
2338
2339
2340
2341/**
2342 * Checks the locking order.
2343 *
2344 * @returns VINF_SUCCESS, VERR_SEM_LV_WRONG_ORDER or VERR_SEM_LV_INTERNAL_ERROR.
2345 * @param pClass The lock class.
2346 * @param uSubClass The lock sub-class.
2347 * @param pThreadSelf The current thread.
2348 * @param pRec The lock record.
2349 * @param pSrcPos The source position of the locking operation.
2350 */
2351static int rtLockValidatorStackCheckLockingOrder(RTLOCKVALCLASSINT * const pClass, uint32_t const uSubClass,
2352 PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION const pRec,
2353 PCRTLOCKVALSRCPOS pSrcPos)
2354{
2355 /*
2356 * Some internal paranoia first.
2357 */
2358 AssertPtr(pClass);
2359 Assert(pClass->u32Magic == RTLOCKVALCLASS_MAGIC);
2360 AssertPtr(pThreadSelf);
2361 Assert(pThreadSelf->u32Magic == RTTHREADINT_MAGIC);
2362 AssertPtr(pRec);
2363 AssertPtrNull(pSrcPos);
2364
2365 /*
2366 * Walk the stack, delegate problems to a worker routine.
2367 */
2368 PRTLOCKVALRECUNION pCur = pThreadSelf->LockValidator.pStackTop;
2369 if (!pCur)
2370 return VINF_SUCCESS;
2371
2372 for (;;)
2373 {
2374 AssertPtrReturn(pCur, VERR_SEM_LV_INTERNAL_ERROR);
2375
2376 if (pCur->Core.u32Magic == RTLOCKVALRECNEST_MAGIC)
2377 pCur = pCur->Nest.pDown;
2378 else
2379 {
2380 uint32_t uPriorSubClass;
2381 PRTLOCKVALRECUNION pDown;
2382 RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
2383 if (pPriorClass != NIL_RTLOCKVALCLASS)
2384 {
2385 AssertPtrReturn(pPriorClass, VERR_SEM_LV_INTERNAL_ERROR);
2386 AssertReturn(pPriorClass->u32Magic == RTLOCKVALCLASS_MAGIC, VERR_SEM_LV_INTERNAL_ERROR);
2387 if (RT_UNLIKELY(!rtLockValidatorIsClassOrderOk(pClass, uSubClass, pPriorClass, uPriorSubClass)))
2388 return rtLockValidatorStackCheckLockingOrder2(pClass, uSubClass, pThreadSelf, pRec, pSrcPos,
2389 pPriorClass, pCur, pDown);
2390 }
2391 pCur = pDown;
2392 }
2393 if (!pCur)
2394 return VINF_SUCCESS;
2395 }
2396}
2397
2398
2399/**
2400 * Check that the lock record is the topmost one on the stack, complain and fail
2401 * if it isn't.
2402 *
2403 * @returns VINF_SUCCESS, VERR_SEM_LV_WRONG_RELEASE_ORDER or
2404 * VERR_SEM_LV_INVALID_PARAMETER.
2405 * @param pThreadSelf The current thread.
2406 * @param pRec The record.
2407 */
2408static int rtLockValidatorStackCheckReleaseOrder(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec)
2409{
2410 AssertReturn(pThreadSelf != NIL_RTTHREAD, VERR_SEM_LV_INVALID_PARAMETER);
2411 Assert(pThreadSelf == RTThreadSelf());
2412
2413 PRTLOCKVALRECUNION pTop = pThreadSelf->LockValidator.pStackTop;
2414 if (RT_LIKELY( pTop == pRec
2415 || ( pTop
2416 && pTop->Core.u32Magic == RTLOCKVALRECNEST_MAGIC
2417 && pTop->Nest.pRec == pRec) ))
2418 return VINF_SUCCESS;
2419
2420#ifdef RTLOCKVAL_WITH_RECURSION_RECORDS
2421 /* Look for a recursion record so the right frame is dumped and marked. */
2422 while (pTop)
2423 {
2424 if (pTop->Core.u32Magic == RTLOCKVALRECNEST_MAGIC)
2425 {
2426 if (pTop->Nest.pRec == pRec)
2427 {
2428 pRec = pTop;
2429 break;
2430 }
2431 pTop = pTop->Nest.pDown;
2432 }
2433 else if (pTop->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC)
2434 pTop = pTop->Excl.pDown;
2435 else if (pTop->Core.u32Magic == RTLOCKVALRECSHRDOWN_MAGIC)
2436 pTop = pTop->ShrdOwner.pDown;
2437 else
2438 break;
2439 }
2440#endif
2441
2442 rtLockValComplainFirst("Wrong release order!", NULL, pThreadSelf, pRec, true);
2443 rtLockValComplainPanic();
2444 return VERR_SEM_LV_WRONG_RELEASE_ORDER;
2445}
2446
2447
2448/**
2449 * Checks if all owners are blocked - shared record operated in signaller mode.
2450 *
2451 * @returns true / false accordingly.
2452 * @param pRec The record.
2453 * @param pThreadSelf The current thread.
2454 */
2455DECL_FORCE_INLINE(bool) rtLockValidatorDdAreAllThreadsBlocked(PRTLOCKVALRECSHRD pRec, PRTTHREADINT pThreadSelf)
2456{
2457 PRTLOCKVALRECSHRDOWN volatile *papOwners = pRec->papOwners;
2458 uint32_t cAllocated = pRec->cAllocated;
2459 uint32_t cEntries = ASMAtomicUoReadU32(&pRec->cEntries);
2460 if (cEntries == 0)
2461 return false;
2462
2463 for (uint32_t i = 0; i < cAllocated; i++)
2464 {
2465 PRTLOCKVALRECSHRDOWN pEntry = rtLockValidatorUoReadSharedOwner(&papOwners[i]);
2466 if ( pEntry
2467 && pEntry->Core.u32Magic == RTLOCKVALRECSHRDOWN_MAGIC)
2468 {
2469 PRTTHREADINT pCurThread = rtLockValidatorReadThreadHandle(&pEntry->hThread);
2470 if (!pCurThread)
2471 return false;
2472 if (pCurThread->u32Magic != RTTHREADINT_MAGIC)
2473 return false;
2474 if ( !RTTHREAD_IS_SLEEPING(rtThreadGetState(pCurThread))
2475 && pCurThread != pThreadSelf)
2476 return false;
2477 if (--cEntries == 0)
2478 break;
2479 }
2480 else
2481 Assert(!pEntry || pEntry->Core.u32Magic == RTLOCKVALRECSHRDOWN_MAGIC_DEAD);
2482 }
2483
2484 return true;
2485}
2486
2487
2488/**
2489 * Verifies the deadlock stack before calling it a deadlock.
2490 *
2491 * @retval VERR_SEM_LV_DEADLOCK if it's a deadlock.
2492 * @retval VERR_SEM_LV_ILLEGAL_UPGRADE if it's a deadlock on the same lock.
2493 * @retval VERR_TRY_AGAIN if something changed.
2494 *
2495 * @param pStack The deadlock detection stack.
2496 * @param pThreadSelf The current thread.
2497 */
2498static int rtLockValidatorDdVerifyDeadlock(PRTLOCKVALDDSTACK pStack, PRTTHREADINT pThreadSelf)
2499{
2500 uint32_t const c = pStack->c;
2501 for (uint32_t iPass = 0; iPass < 3; iPass++)
2502 {
2503 for (uint32_t i = 1; i < c; i++)
2504 {
2505 PRTTHREADINT pThread = pStack->a[i].pThread;
2506 if (pThread->u32Magic != RTTHREADINT_MAGIC)
2507 return VERR_TRY_AGAIN;
2508 if (rtThreadGetState(pThread) != pStack->a[i].enmState)
2509 return VERR_TRY_AGAIN;
2510 if (rtLockValidatorReadRecUnionPtr(&pThread->LockValidator.pRec) != pStack->a[i].pFirstSibling)
2511 return VERR_TRY_AGAIN;
2512 /* ASSUMES the signaller records won't have siblings! */
2513 PRTLOCKVALRECUNION pRec = pStack->a[i].pRec;
2514 if ( pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
2515 && pRec->Shared.fSignaller
2516 && !rtLockValidatorDdAreAllThreadsBlocked(&pRec->Shared, pThreadSelf))
2517 return VERR_TRY_AGAIN;
2518 }
2519 RTThreadYield();
2520 }
2521
2522 if (c == 1)
2523 return VERR_SEM_LV_ILLEGAL_UPGRADE;
2524 return VERR_SEM_LV_DEADLOCK;
2525}
2526
2527
2528/**
2529 * Checks for stack cycles caused by another deadlock before returning.
2530 *
2531 * @retval VINF_SUCCESS if the stack is simply too small.
2532 * @retval VERR_SEM_LV_EXISTING_DEADLOCK if a cycle was detected.
2533 *
2534 * @param pStack The deadlock detection stack.
2535 */
2536static int rtLockValidatorDdHandleStackOverflow(PRTLOCKVALDDSTACK pStack)
2537{
2538 for (size_t i = 0; i < RT_ELEMENTS(pStack->a) - 1; i++)
2539 {
2540 PRTTHREADINT pThread = pStack->a[i].pThread;
2541 for (size_t j = i + 1; j < RT_ELEMENTS(pStack->a); j++)
2542 if (pStack->a[j].pThread == pThread)
2543 return VERR_SEM_LV_EXISTING_DEADLOCK;
2544 }
2545 static bool volatile s_fComplained = false;
2546 if (!s_fComplained)
2547 {
2548 s_fComplained = true;
2549 rtLockValComplain(RT_SRC_POS, "lock validator stack is too small! (%zu entries)\n", RT_ELEMENTS(pStack->a));
2550 }
2551 return VINF_SUCCESS;
2552}
2553
2554
2555/**
2556 * Worker for rtLockValidatorDeadlockDetection that does the actual deadlock
2557 * detection.
2558 *
2559 * @retval VINF_SUCCESS
2560 * @retval VERR_SEM_LV_DEADLOCK
2561 * @retval VERR_SEM_LV_EXISTING_DEADLOCK
2562 * @retval VERR_SEM_LV_ILLEGAL_UPGRADE
2563 * @retval VERR_TRY_AGAIN
2564 *
2565 * @param pStack The stack to use.
2566 * @param pOriginalRec The original record.
2567 * @param pThreadSelf The calling thread.
2568 */
2569static int rtLockValidatorDdDoDetection(PRTLOCKVALDDSTACK pStack, PRTLOCKVALRECUNION const pOriginalRec,
2570 PRTTHREADINT const pThreadSelf)
2571{
2572 pStack->c = 0;
2573
2574 /* We could use a single RTLOCKVALDDENTRY variable here, but the
2575 compiler may make a better job of it when using individual variables. */
2576 PRTLOCKVALRECUNION pRec = pOriginalRec;
2577 PRTLOCKVALRECUNION pFirstSibling = pOriginalRec;
2578 uint32_t iEntry = UINT32_MAX;
2579 PRTTHREADINT pThread = NIL_RTTHREAD;
2580 RTTHREADSTATE enmState = RTTHREADSTATE_RUNNING;
2581 for (uint32_t iLoop = 0; ; iLoop++)
2582 {
2583 /*
2584 * Process the current record.
2585 */
2586 RTLOCKVAL_ASSERT_PTR_ALIGN(pRec);
2587
2588 /* Find the next relevant owner thread and record. */
2589 PRTLOCKVALRECUNION pNextRec = NULL;
2590 RTTHREADSTATE enmNextState = RTTHREADSTATE_RUNNING;
2591 PRTTHREADINT pNextThread = NIL_RTTHREAD;
2592 switch (pRec->Core.u32Magic)
2593 {
2594 case RTLOCKVALRECEXCL_MAGIC:
2595 Assert(iEntry == UINT32_MAX);
2596 for (;;)
2597 {
2598 pNextThread = rtLockValidatorReadThreadHandle(&pRec->Excl.hThread);
2599 if ( !pNextThread
2600 || pNextThread->u32Magic != RTTHREADINT_MAGIC)
2601 break;
2602 enmNextState = rtThreadGetState(pNextThread);
2603 if ( !RTTHREAD_IS_SLEEPING(enmNextState)
2604 && pNextThread != pThreadSelf)
2605 break;
2606 pNextRec = rtLockValidatorReadRecUnionPtr(&pNextThread->LockValidator.pRec);
2607 if (RT_LIKELY( !pNextRec
2608 || enmNextState == rtThreadGetState(pNextThread)))
2609 break;
2610 pNextRec = NULL;
2611 }
2612 if (!pNextRec)
2613 {
2614 pRec = pRec->Excl.pSibling;
2615 if ( pRec
2616 && pRec != pFirstSibling)
2617 continue;
2618 pNextThread = NIL_RTTHREAD;
2619 }
2620 break;
2621
2622 case RTLOCKVALRECSHRD_MAGIC:
2623 if (!pRec->Shared.fSignaller)
2624 {
2625 /* Skip to the next sibling if same side. ASSUMES reader priority. */
2626 /** @todo The read side of a read-write lock is problematic if
2627 * the implementation prioritizes writers over readers because
2628 * that means we should could deadlock against current readers
2629 * if a writer showed up. If the RW sem implementation is
2630 * wrapping some native API, it's not so easy to detect when we
2631 * should do this and when we shouldn't. Checking when we
2632 * shouldn't is subject to wakeup scheduling and cannot easily
2633 * be made reliable.
2634 *
2635 * At the moment we circumvent all this mess by declaring that
2636 * readers has priority. This is TRUE on linux, but probably
2637 * isn't on Solaris and FreeBSD. */
2638 if ( pRec == pFirstSibling
2639 && pRec->Shared.pSibling != NULL
2640 && pRec->Shared.pSibling != pFirstSibling)
2641 {
2642 pRec = pRec->Shared.pSibling;
2643 Assert(iEntry == UINT32_MAX);
2644 continue;
2645 }
2646 }
2647
2648 /* Scan the owner table for blocked owners. */
2649 if ( ASMAtomicUoReadU32(&pRec->Shared.cEntries) > 0
2650 && ( !pRec->Shared.fSignaller
2651 || iEntry != UINT32_MAX
2652 || rtLockValidatorDdAreAllThreadsBlocked(&pRec->Shared, pThreadSelf)
2653 )
2654 )
2655 {
2656 uint32_t cAllocated = pRec->Shared.cAllocated;
2657 PRTLOCKVALRECSHRDOWN volatile *papOwners = pRec->Shared.papOwners;
2658 while (++iEntry < cAllocated)
2659 {
2660 PRTLOCKVALRECSHRDOWN pEntry = rtLockValidatorUoReadSharedOwner(&papOwners[iEntry]);
2661 if (pEntry)
2662 {
2663 for (;;)
2664 {
2665 if (pEntry->Core.u32Magic != RTLOCKVALRECSHRDOWN_MAGIC)
2666 break;
2667 pNextThread = rtLockValidatorReadThreadHandle(&pEntry->hThread);
2668 if ( !pNextThread
2669 || pNextThread->u32Magic != RTTHREADINT_MAGIC)
2670 break;
2671 enmNextState = rtThreadGetState(pNextThread);
2672 if ( !RTTHREAD_IS_SLEEPING(enmNextState)
2673 && pNextThread != pThreadSelf)
2674 break;
2675 pNextRec = rtLockValidatorReadRecUnionPtr(&pNextThread->LockValidator.pRec);
2676 if (RT_LIKELY( !pNextRec
2677 || enmNextState == rtThreadGetState(pNextThread)))
2678 break;
2679 pNextRec = NULL;
2680 }
2681 if (pNextRec)
2682 break;
2683 }
2684 else
2685 Assert(!pEntry || pEntry->Core.u32Magic == RTLOCKVALRECSHRDOWN_MAGIC_DEAD);
2686 }
2687 if (pNextRec)
2688 break;
2689 pNextThread = NIL_RTTHREAD;
2690 }
2691
2692 /* Advance to the next sibling, if any. */
2693 pRec = pRec->Shared.pSibling;
2694 if ( pRec != NULL
2695 && pRec != pFirstSibling)
2696 {
2697 iEntry = UINT32_MAX;
2698 continue;
2699 }
2700 break;
2701
2702 case RTLOCKVALRECEXCL_MAGIC_DEAD:
2703 case RTLOCKVALRECSHRD_MAGIC_DEAD:
2704 break;
2705
2706 case RTLOCKVALRECSHRDOWN_MAGIC:
2707 case RTLOCKVALRECSHRDOWN_MAGIC_DEAD:
2708 default:
2709 AssertMsgFailed(("%p: %#x\n", pRec, pRec->Core));
2710 break;
2711 }
2712
2713 if (pNextRec)
2714 {
2715 /*
2716 * Recurse and check for deadlock.
2717 */
2718 uint32_t i = pStack->c;
2719 if (RT_UNLIKELY(i >= RT_ELEMENTS(pStack->a)))
2720 return rtLockValidatorDdHandleStackOverflow(pStack);
2721
2722 pStack->c++;
2723 pStack->a[i].pRec = pRec;
2724 pStack->a[i].iEntry = iEntry;
2725 pStack->a[i].enmState = enmState;
2726 pStack->a[i].pThread = pThread;
2727 pStack->a[i].pFirstSibling = pFirstSibling;
2728
2729 if (RT_UNLIKELY( pNextThread == pThreadSelf
2730 && ( i != 0
2731 || pRec->Core.u32Magic != RTLOCKVALRECSHRD_MAGIC
2732 || !pRec->Shared.fSignaller) /* ASSUMES signaller records have no siblings. */
2733 )
2734 )
2735 return rtLockValidatorDdVerifyDeadlock(pStack, pThreadSelf);
2736
2737 pRec = pNextRec;
2738 pFirstSibling = pNextRec;
2739 iEntry = UINT32_MAX;
2740 enmState = enmNextState;
2741 pThread = pNextThread;
2742 }
2743 else
2744 {
2745 /*
2746 * No deadlock here, unwind the stack and deal with any unfinished
2747 * business there.
2748 */
2749 uint32_t i = pStack->c;
2750 for (;;)
2751 {
2752 /* pop */
2753 if (i == 0)
2754 return VINF_SUCCESS;
2755 i--;
2756 pRec = pStack->a[i].pRec;
2757 iEntry = pStack->a[i].iEntry;
2758
2759 /* Examine it. */
2760 uint32_t u32Magic = pRec->Core.u32Magic;
2761 if (u32Magic == RTLOCKVALRECEXCL_MAGIC)
2762 pRec = pRec->Excl.pSibling;
2763 else if (u32Magic == RTLOCKVALRECSHRD_MAGIC)
2764 {
2765 if (iEntry + 1 < pRec->Shared.cAllocated)
2766 break; /* continue processing this record. */
2767 pRec = pRec->Shared.pSibling;
2768 }
2769 else
2770 {
2771 Assert( u32Magic == RTLOCKVALRECEXCL_MAGIC_DEAD
2772 || u32Magic == RTLOCKVALRECSHRD_MAGIC_DEAD);
2773 continue;
2774 }
2775
2776 /* Any next record to advance to? */
2777 if ( !pRec
2778 || pRec == pStack->a[i].pFirstSibling)
2779 continue;
2780 iEntry = UINT32_MAX;
2781 break;
2782 }
2783
2784 /* Restore the rest of the state and update the stack. */
2785 pFirstSibling = pStack->a[i].pFirstSibling;
2786 enmState = pStack->a[i].enmState;
2787 pThread = pStack->a[i].pThread;
2788 pStack->c = i;
2789 }
2790
2791 Assert(iLoop != 1000000);
2792 }
2793}
2794
2795
2796/**
2797 * Check for the simple no-deadlock case.
2798 *
2799 * @returns true if no deadlock, false if further investigation is required.
2800 *
2801 * @param pOriginalRec The original record.
2802 */
2803DECLINLINE(int) rtLockValidatorIsSimpleNoDeadlockCase(PRTLOCKVALRECUNION pOriginalRec)
2804{
2805 if ( pOriginalRec->Excl.Core.u32Magic == RTLOCKVALRECEXCL_MAGIC
2806 && !pOriginalRec->Excl.pSibling)
2807 {
2808 PRTTHREADINT pThread = rtLockValidatorReadThreadHandle(&pOriginalRec->Excl.hThread);
2809 if ( !pThread
2810 || pThread->u32Magic != RTTHREADINT_MAGIC)
2811 return true;
2812 RTTHREADSTATE enmState = rtThreadGetState(pThread);
2813 if (!RTTHREAD_IS_SLEEPING(enmState))
2814 return true;
2815 }
2816 return false;
2817}
2818
2819
2820/**
2821 * Worker for rtLockValidatorDeadlockDetection that bitches about a deadlock.
2822 *
2823 * @param pStack The chain of locks causing the deadlock.
2824 * @param pRec The record relating to the current thread's lock
2825 * operation.
2826 * @param pThreadSelf This thread.
2827 * @param pSrcPos Where we are going to deadlock.
2828 * @param rc The return code.
2829 */
2830static void rcLockValidatorDoDeadlockComplaining(PRTLOCKVALDDSTACK pStack, PRTLOCKVALRECUNION pRec,
2831 PRTTHREADINT pThreadSelf, PCRTLOCKVALSRCPOS pSrcPos, int rc)
2832{
2833 if (!ASMAtomicUoReadBool(&g_fLockValidatorQuiet))
2834 {
2835 const char *pszWhat;
2836 switch (rc)
2837 {
2838 case VERR_SEM_LV_DEADLOCK: pszWhat = "Detected deadlock!"; break;
2839 case VERR_SEM_LV_EXISTING_DEADLOCK: pszWhat = "Found existing deadlock!"; break;
2840 case VERR_SEM_LV_ILLEGAL_UPGRADE: pszWhat = "Illegal lock upgrade!"; break;
2841 default: AssertFailed(); pszWhat = "!unexpected rc!"; break;
2842 }
2843 rtLockValComplainFirst(pszWhat, pSrcPos, pThreadSelf, pStack->a[0].pRec != pRec ? pRec : NULL, true);
2844 rtLockValComplainMore("---- start of deadlock chain - %u entries ----\n", pStack->c);
2845 for (uint32_t i = 0; i < pStack->c; i++)
2846 {
2847 char szPrefix[24];
2848 RTStrPrintf(szPrefix, sizeof(szPrefix), "#%02u: ", i);
2849 PRTLOCKVALRECUNION pShrdOwner = NULL;
2850 if (pStack->a[i].pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC)
2851 pShrdOwner = (PRTLOCKVALRECUNION)pStack->a[i].pRec->Shared.papOwners[pStack->a[i].iEntry];
2852 if (VALID_PTR(pShrdOwner) && pShrdOwner->Core.u32Magic == RTLOCKVALRECSHRDOWN_MAGIC)
2853 {
2854 rtLockValComplainAboutLock(szPrefix, pShrdOwner, "\n");
2855 rtLockValComplainAboutLockStack(pShrdOwner->ShrdOwner.hThread, 5, 2, pShrdOwner);
2856 }
2857 else
2858 {
2859 rtLockValComplainAboutLock(szPrefix, pStack->a[i].pRec, "\n");
2860 if (pStack->a[i].pRec->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC)
2861 rtLockValComplainAboutLockStack(pStack->a[i].pRec->Excl.hThread, 5, 2, pStack->a[i].pRec);
2862 }
2863 }
2864 rtLockValComplainMore("---- end of deadlock chain ----\n");
2865 }
2866
2867 rtLockValComplainPanic();
2868}
2869
2870
2871/**
2872 * Perform deadlock detection.
2873 *
2874 * @retval VINF_SUCCESS
2875 * @retval VERR_SEM_LV_DEADLOCK
2876 * @retval VERR_SEM_LV_EXISTING_DEADLOCK
2877 * @retval VERR_SEM_LV_ILLEGAL_UPGRADE
2878 *
2879 * @param pRec The record relating to the current thread's lock
2880 * operation.
2881 * @param pThreadSelf The current thread.
2882 * @param pSrcPos The position of the current lock operation.
2883 */
2884static int rtLockValidatorDeadlockDetection(PRTLOCKVALRECUNION pRec, PRTTHREADINT pThreadSelf, PCRTLOCKVALSRCPOS pSrcPos)
2885{
2886 RTLOCKVALDDSTACK Stack;
2887 int rc = rtLockValidatorDdDoDetection(&Stack, pRec, pThreadSelf);
2888 if (RT_SUCCESS(rc))
2889 return VINF_SUCCESS;
2890
2891 if (rc == VERR_TRY_AGAIN)
2892 {
2893 for (uint32_t iLoop = 0; ; iLoop++)
2894 {
2895 rc = rtLockValidatorDdDoDetection(&Stack, pRec, pThreadSelf);
2896 if (RT_SUCCESS_NP(rc))
2897 return VINF_SUCCESS;
2898 if (rc != VERR_TRY_AGAIN)
2899 break;
2900 RTThreadYield();
2901 if (iLoop >= 3)
2902 return VINF_SUCCESS;
2903 }
2904 }
2905
2906 rcLockValidatorDoDeadlockComplaining(&Stack, pRec, pThreadSelf, pSrcPos, rc);
2907 return rc;
2908}
2909
2910
2911RTDECL(void) RTLockValidatorRecExclInitV(PRTLOCKVALRECEXCL pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
2912 void *hLock, bool fEnabled, const char *pszNameFmt, va_list va)
2913{
2914 RTLOCKVAL_ASSERT_PTR_ALIGN(pRec);
2915 RTLOCKVAL_ASSERT_PTR_ALIGN(hLock);
2916 Assert( uSubClass >= RTLOCKVAL_SUB_CLASS_USER
2917 || uSubClass == RTLOCKVAL_SUB_CLASS_NONE
2918 || uSubClass == RTLOCKVAL_SUB_CLASS_ANY);
2919
2920 pRec->Core.u32Magic = RTLOCKVALRECEXCL_MAGIC;
2921 pRec->fEnabled = fEnabled && RTLockValidatorIsEnabled();
2922 pRec->afReserved[0] = 0;
2923 pRec->afReserved[1] = 0;
2924 pRec->afReserved[2] = 0;
2925 rtLockValidatorSrcPosInit(&pRec->SrcPos);
2926 pRec->hThread = NIL_RTTHREAD;
2927 pRec->pDown = NULL;
2928 pRec->hClass = rtLockValidatorClassValidateAndRetain(hClass);
2929 pRec->uSubClass = uSubClass;
2930 pRec->cRecursion = 0;
2931 pRec->hLock = hLock;
2932 pRec->pSibling = NULL;
2933 if (pszNameFmt)
2934 RTStrPrintfV(pRec->szName, sizeof(pRec->szName), pszNameFmt, va);
2935 else
2936 {
2937 static uint32_t volatile s_cAnonymous = 0;
2938 uint32_t i = ASMAtomicIncU32(&s_cAnonymous) - 1;
2939 RTStrPrintf(pRec->szName, sizeof(pRec->szName), "anon-excl-%u", i);
2940 }
2941
2942 /* Lazy initialization. */
2943 if (RT_UNLIKELY(g_hLockValidatorXRoads == NIL_RTSEMXROADS))
2944 rtLockValidatorLazyInit();
2945}
2946
2947
2948RTDECL(void) RTLockValidatorRecExclInit(PRTLOCKVALRECEXCL pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
2949 void *hLock, bool fEnabled, const char *pszNameFmt, ...)
2950{
2951 va_list va;
2952 va_start(va, pszNameFmt);
2953 RTLockValidatorRecExclInitV(pRec, hClass, uSubClass, hLock, fEnabled, pszNameFmt, va);
2954 va_end(va);
2955}
2956
2957
2958RTDECL(int) RTLockValidatorRecExclCreateV(PRTLOCKVALRECEXCL *ppRec, RTLOCKVALCLASS hClass,
2959 uint32_t uSubClass, void *pvLock, bool fEnabled,
2960 const char *pszNameFmt, va_list va)
2961{
2962 PRTLOCKVALRECEXCL pRec;
2963 *ppRec = pRec = (PRTLOCKVALRECEXCL)RTMemAlloc(sizeof(*pRec));
2964 if (!pRec)
2965 return VERR_NO_MEMORY;
2966 RTLockValidatorRecExclInitV(pRec, hClass, uSubClass, pvLock, fEnabled, pszNameFmt, va);
2967 return VINF_SUCCESS;
2968}
2969
2970
2971RTDECL(int) RTLockValidatorRecExclCreate(PRTLOCKVALRECEXCL *ppRec, RTLOCKVALCLASS hClass,
2972 uint32_t uSubClass, void *pvLock, bool fEnabled,
2973 const char *pszNameFmt, ...)
2974{
2975 va_list va;
2976 va_start(va, pszNameFmt);
2977 int rc = RTLockValidatorRecExclCreateV(ppRec, hClass, uSubClass, pvLock, fEnabled, pszNameFmt, va);
2978 va_end(va);
2979 return rc;
2980}
2981
2982
2983RTDECL(void) RTLockValidatorRecExclDelete(PRTLOCKVALRECEXCL pRec)
2984{
2985 Assert(pRec->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC);
2986
2987 rtLockValidatorSerializeDestructEnter();
2988
2989 ASMAtomicWriteU32(&pRec->Core.u32Magic, RTLOCKVALRECEXCL_MAGIC_DEAD);
2990 ASMAtomicWriteHandle(&pRec->hThread, NIL_RTTHREAD);
2991 RTLOCKVALCLASS hClass;
2992 ASMAtomicXchgHandle(&pRec->hClass, NIL_RTLOCKVALCLASS, &hClass);
2993 if (pRec->pSibling)
2994 rtLockValidatorUnlinkAllSiblings(&pRec->Core);
2995 rtLockValidatorSerializeDestructLeave();
2996 if (hClass != NIL_RTLOCKVALCLASS)
2997 RTLockValidatorClassRelease(hClass);
2998}
2999
3000
3001RTDECL(void) RTLockValidatorRecExclDestroy(PRTLOCKVALRECEXCL *ppRec)
3002{
3003 PRTLOCKVALRECEXCL pRec = *ppRec;
3004 *ppRec = NULL;
3005 if (pRec)
3006 {
3007 RTLockValidatorRecExclDelete(pRec);
3008 RTMemFree(pRec);
3009 }
3010}
3011
3012
3013RTDECL(uint32_t) RTLockValidatorRecExclSetSubClass(PRTLOCKVALRECEXCL pRec, uint32_t uSubClass)
3014{
3015 AssertPtrReturn(pRec, RTLOCKVAL_SUB_CLASS_INVALID);
3016 AssertReturn(pRec->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
3017 AssertReturn( uSubClass >= RTLOCKVAL_SUB_CLASS_USER
3018 || uSubClass == RTLOCKVAL_SUB_CLASS_NONE
3019 || uSubClass == RTLOCKVAL_SUB_CLASS_ANY,
3020 RTLOCKVAL_SUB_CLASS_INVALID);
3021 return ASMAtomicXchgU32(&pRec->uSubClass, uSubClass);
3022}
3023
3024
3025RTDECL(void) RTLockValidatorRecExclSetOwner(PRTLOCKVALRECEXCL pRec, RTTHREAD hThreadSelf,
3026 PCRTLOCKVALSRCPOS pSrcPos, bool fFirstRecursion)
3027{
3028 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3029 AssertReturnVoid(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC);
3030 if (!pRecU->Excl.fEnabled)
3031 return;
3032 if (hThreadSelf == NIL_RTTHREAD)
3033 {
3034 hThreadSelf = RTThreadSelfAutoAdopt();
3035 AssertReturnVoid(hThreadSelf != NIL_RTTHREAD);
3036 }
3037 AssertReturnVoid(hThreadSelf->u32Magic == RTTHREADINT_MAGIC);
3038 Assert(hThreadSelf == RTThreadSelf());
3039
3040 ASMAtomicIncS32(&hThreadSelf->LockValidator.cWriteLocks);
3041
3042 if (pRecU->Excl.hThread == hThreadSelf)
3043 {
3044 Assert(!fFirstRecursion);
3045 pRecU->Excl.cRecursion++;
3046 rtLockValidatorStackPushRecursion(hThreadSelf, pRecU, pSrcPos);
3047 }
3048 else
3049 {
3050 Assert(pRecU->Excl.hThread == NIL_RTTHREAD);
3051
3052 rtLockValidatorSrcPosCopy(&pRecU->Excl.SrcPos, pSrcPos);
3053 ASMAtomicUoWriteU32(&pRecU->Excl.cRecursion, 1);
3054 ASMAtomicWriteHandle(&pRecU->Excl.hThread, hThreadSelf);
3055
3056 rtLockValidatorStackPush(hThreadSelf, pRecU);
3057 }
3058}
3059
3060
3061/**
3062 * Internal worker for RTLockValidatorRecExclReleaseOwner and
3063 * RTLockValidatorRecExclReleaseOwnerUnchecked.
3064 */
3065static void rtLockValidatorRecExclReleaseOwnerUnchecked(PRTLOCKVALRECUNION pRec, bool fFinalRecursion)
3066{
3067 RTTHREADINT *pThread = pRec->Excl.hThread;
3068 AssertReturnVoid(pThread != NIL_RTTHREAD);
3069 Assert(pThread == RTThreadSelf());
3070
3071 ASMAtomicDecS32(&pThread->LockValidator.cWriteLocks);
3072 uint32_t c = ASMAtomicDecU32(&pRec->Excl.cRecursion);
3073 if (c == 0)
3074 {
3075 rtLockValidatorStackPop(pThread, pRec);
3076 ASMAtomicWriteHandle(&pRec->Excl.hThread, NIL_RTTHREAD);
3077 }
3078 else
3079 {
3080 Assert(c < UINT32_C(0xffff0000));
3081 Assert(!fFinalRecursion);
3082 rtLockValidatorStackPopRecursion(pThread, pRec);
3083 }
3084}
3085
3086RTDECL(int) RTLockValidatorRecExclReleaseOwner(PRTLOCKVALRECEXCL pRec, bool fFinalRecursion)
3087{
3088 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3089 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3090 if (!pRecU->Excl.fEnabled)
3091 return VINF_SUCCESS;
3092
3093 /*
3094 * Check the release order.
3095 */
3096 if ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3097 && pRecU->Excl.hClass->fStrictReleaseOrder
3098 && pRecU->Excl.hClass->cMsMinOrder != RT_INDEFINITE_WAIT
3099 )
3100 {
3101 int rc = rtLockValidatorStackCheckReleaseOrder(pRecU->Excl.hThread, pRecU);
3102 if (RT_FAILURE(rc))
3103 return rc;
3104 }
3105
3106 /*
3107 * Join paths with RTLockValidatorRecExclReleaseOwnerUnchecked.
3108 */
3109 rtLockValidatorRecExclReleaseOwnerUnchecked(pRecU, fFinalRecursion);
3110 return VINF_SUCCESS;
3111}
3112
3113
3114RTDECL(void) RTLockValidatorRecExclReleaseOwnerUnchecked(PRTLOCKVALRECEXCL pRec)
3115{
3116 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3117 AssertReturnVoid(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC);
3118 if (pRecU->Excl.fEnabled)
3119 rtLockValidatorRecExclReleaseOwnerUnchecked(pRecU, false);
3120}
3121
3122
3123RTDECL(int) RTLockValidatorRecExclRecursion(PRTLOCKVALRECEXCL pRec, PCRTLOCKVALSRCPOS pSrcPos)
3124{
3125 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3126 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3127 if (!pRecU->Excl.fEnabled)
3128 return VINF_SUCCESS;
3129 AssertReturn(pRecU->Excl.hThread != NIL_RTTHREAD, VERR_SEM_LV_INVALID_PARAMETER);
3130 AssertReturn(pRecU->Excl.cRecursion > 0, VERR_SEM_LV_INVALID_PARAMETER);
3131
3132 if ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3133 && !pRecU->Excl.hClass->fRecursionOk)
3134 {
3135 rtLockValComplainFirst("Recursion not allowed by the class!",
3136 pSrcPos, pRecU->Excl.hThread, (PRTLOCKVALRECUNION)pRec, true);
3137 rtLockValComplainPanic();
3138 return VERR_SEM_LV_NESTED;
3139 }
3140
3141 Assert(pRecU->Excl.cRecursion < _1M);
3142 pRecU->Excl.cRecursion++;
3143 rtLockValidatorStackPushRecursion(pRecU->Excl.hThread, pRecU, pSrcPos);
3144 return VINF_SUCCESS;
3145}
3146
3147
3148RTDECL(int) RTLockValidatorRecExclUnwind(PRTLOCKVALRECEXCL pRec)
3149{
3150 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3151 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3152 if (!pRecU->Excl.fEnabled)
3153 return VINF_SUCCESS;
3154 AssertReturn(pRecU->Excl.hThread != NIL_RTTHREAD, VERR_SEM_LV_INVALID_PARAMETER);
3155 Assert(pRecU->Excl.hThread == RTThreadSelf());
3156 AssertReturn(pRecU->Excl.cRecursion > 1, VERR_SEM_LV_INVALID_PARAMETER);
3157
3158 /*
3159 * Check the release order.
3160 */
3161 if ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3162 && pRecU->Excl.hClass->fStrictReleaseOrder
3163 && pRecU->Excl.hClass->cMsMinOrder != RT_INDEFINITE_WAIT
3164 )
3165 {
3166 int rc = rtLockValidatorStackCheckReleaseOrder(pRecU->Excl.hThread, pRecU);
3167 if (RT_FAILURE(rc))
3168 return rc;
3169 }
3170
3171 /*
3172 * Perform the unwind.
3173 */
3174 pRecU->Excl.cRecursion--;
3175 rtLockValidatorStackPopRecursion(pRecU->Excl.hThread, pRecU);
3176 return VINF_SUCCESS;
3177}
3178
3179
3180RTDECL(int) RTLockValidatorRecExclRecursionMixed(PRTLOCKVALRECEXCL pRec, PRTLOCKVALRECCORE pRecMixed, PCRTLOCKVALSRCPOS pSrcPos)
3181{
3182 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3183 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3184 PRTLOCKVALRECUNION pRecMixedU = (PRTLOCKVALRECUNION)pRecMixed;
3185 AssertReturn( pRecMixedU->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
3186 || pRecMixedU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC
3187 , VERR_SEM_LV_INVALID_PARAMETER);
3188 if (!pRecU->Excl.fEnabled)
3189 return VINF_SUCCESS;
3190 Assert(pRecU->Excl.hThread == RTThreadSelf());
3191 AssertReturn(pRecU->Excl.hThread != NIL_RTTHREAD, VERR_SEM_LV_INVALID_PARAMETER);
3192 AssertReturn(pRecU->Excl.cRecursion > 0, VERR_SEM_LV_INVALID_PARAMETER);
3193
3194 if ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3195 && !pRecU->Excl.hClass->fRecursionOk)
3196 {
3197 rtLockValComplainFirst("Mixed recursion not allowed by the class!",
3198 pSrcPos, pRecU->Excl.hThread, (PRTLOCKVALRECUNION)pRec, true);
3199 rtLockValComplainPanic();
3200 return VERR_SEM_LV_NESTED;
3201 }
3202
3203 Assert(pRecU->Excl.cRecursion < _1M);
3204 pRecU->Excl.cRecursion++;
3205 rtLockValidatorStackPushRecursion(pRecU->Excl.hThread, pRecU, pSrcPos);
3206
3207 return VINF_SUCCESS;
3208}
3209
3210
3211RTDECL(int) RTLockValidatorRecExclUnwindMixed(PRTLOCKVALRECEXCL pRec, PRTLOCKVALRECCORE pRecMixed)
3212{
3213 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3214 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3215 PRTLOCKVALRECUNION pRecMixedU = (PRTLOCKVALRECUNION)pRecMixed;
3216 AssertReturn( pRecMixedU->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
3217 || pRecMixedU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC
3218 , VERR_SEM_LV_INVALID_PARAMETER);
3219 if (!pRecU->Excl.fEnabled)
3220 return VINF_SUCCESS;
3221 Assert(pRecU->Excl.hThread == RTThreadSelf());
3222 AssertReturn(pRecU->Excl.hThread != NIL_RTTHREAD, VERR_SEM_LV_INVALID_PARAMETER);
3223 AssertReturn(pRecU->Excl.cRecursion > 1, VERR_SEM_LV_INVALID_PARAMETER);
3224
3225 /*
3226 * Check the release order.
3227 */
3228 if ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3229 && pRecU->Excl.hClass->fStrictReleaseOrder
3230 && pRecU->Excl.hClass->cMsMinOrder != RT_INDEFINITE_WAIT
3231 )
3232 {
3233 int rc = rtLockValidatorStackCheckReleaseOrder(pRecU->Excl.hThread, pRecU);
3234 if (RT_FAILURE(rc))
3235 return rc;
3236 }
3237
3238 /*
3239 * Perform the unwind.
3240 */
3241 pRecU->Excl.cRecursion--;
3242 rtLockValidatorStackPopRecursion(pRecU->Excl.hThread, pRecU);
3243 return VINF_SUCCESS;
3244}
3245
3246
3247RTDECL(int) RTLockValidatorRecExclCheckOrder(PRTLOCKVALRECEXCL pRec, RTTHREAD hThreadSelf,
3248 PCRTLOCKVALSRCPOS pSrcPos, RTMSINTERVAL cMillies)
3249{
3250 /*
3251 * Validate and adjust input. Quit early if order validation is disabled.
3252 */
3253 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3254 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3255 if ( !pRecU->Excl.fEnabled
3256 || pRecU->Excl.hClass == NIL_RTLOCKVALCLASS
3257 || pRecU->Excl.hClass->cMsMinOrder == RT_INDEFINITE_WAIT
3258 || pRecU->Excl.hClass->cMsMinOrder > cMillies)
3259 return VINF_SUCCESS;
3260
3261 if (hThreadSelf == NIL_RTTHREAD)
3262 {
3263 hThreadSelf = RTThreadSelfAutoAdopt();
3264 AssertReturn(hThreadSelf != NIL_RTTHREAD, VERR_SEM_LV_INTERNAL_ERROR);
3265 }
3266 AssertReturn(hThreadSelf->u32Magic == RTTHREADINT_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3267 Assert(hThreadSelf == RTThreadSelf());
3268
3269 /*
3270 * Detect recursion as it isn't subject to order restrictions.
3271 */
3272 if (pRec->hThread == hThreadSelf)
3273 return VINF_SUCCESS;
3274
3275 return rtLockValidatorStackCheckLockingOrder(pRecU->Excl.hClass, pRecU->Excl.uSubClass, hThreadSelf, pRecU, pSrcPos);
3276}
3277
3278
3279RTDECL(int) RTLockValidatorRecExclCheckBlocking(PRTLOCKVALRECEXCL pRec, RTTHREAD hThreadSelf,
3280 PCRTLOCKVALSRCPOS pSrcPos, bool fRecursiveOk, RTMSINTERVAL cMillies,
3281 RTTHREADSTATE enmSleepState, bool fReallySleeping)
3282{
3283 /*
3284 * Fend off wild life.
3285 */
3286 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3287 AssertPtrReturn(pRecU, VERR_SEM_LV_INVALID_PARAMETER);
3288 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECEXCL_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3289 if (!pRec->fEnabled)
3290 return VINF_SUCCESS;
3291
3292 PRTTHREADINT pThreadSelf = hThreadSelf;
3293 AssertPtrReturn(pThreadSelf, VERR_SEM_LV_INVALID_PARAMETER);
3294 AssertReturn(pThreadSelf->u32Magic == RTTHREADINT_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3295 Assert(pThreadSelf == RTThreadSelf());
3296
3297 AssertReturn(RTTHREAD_IS_SLEEPING(enmSleepState), VERR_SEM_LV_INVALID_PARAMETER);
3298
3299 RTTHREADSTATE enmThreadState = rtThreadGetState(pThreadSelf);
3300 if (RT_UNLIKELY(enmThreadState != RTTHREADSTATE_RUNNING))
3301 {
3302 AssertReturn( enmThreadState == RTTHREADSTATE_TERMINATED /* rtThreadRemove uses locks too */
3303 || enmThreadState == RTTHREADSTATE_INITIALIZING /* rtThreadInsert uses locks too */
3304 , VERR_SEM_LV_INVALID_PARAMETER);
3305 enmSleepState = enmThreadState;
3306 }
3307
3308 /*
3309 * Record the location.
3310 */
3311 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pRec, pRecU);
3312 rtLockValidatorSrcPosCopy(&pThreadSelf->LockValidator.SrcPos, pSrcPos);
3313 ASMAtomicWriteBool(&pThreadSelf->LockValidator.fInValidator, true);
3314 pThreadSelf->LockValidator.enmRecState = enmSleepState;
3315 rtThreadSetState(pThreadSelf, enmSleepState);
3316
3317 /*
3318 * Don't do deadlock detection if we're recursing.
3319 *
3320 * On some hosts we don't do recursion accounting our selves and there
3321 * isn't any other place to check for this.
3322 */
3323 int rc = VINF_SUCCESS;
3324 if (rtLockValidatorReadThreadHandle(&pRecU->Excl.hThread) == pThreadSelf)
3325 {
3326 if ( !fRecursiveOk
3327 || ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3328 && !pRecU->Excl.hClass->fRecursionOk))
3329 {
3330 rtLockValComplainFirst("Recursion not allowed!", pSrcPos, pThreadSelf, pRecU, true);
3331 rtLockValComplainPanic();
3332 rc = VERR_SEM_LV_NESTED;
3333 }
3334 }
3335 /*
3336 * Perform deadlock detection.
3337 */
3338 else if ( pRecU->Excl.hClass != NIL_RTLOCKVALCLASS
3339 && ( pRecU->Excl.hClass->cMsMinDeadlock > cMillies
3340 || pRecU->Excl.hClass->cMsMinDeadlock > RT_INDEFINITE_WAIT))
3341 rc = VINF_SUCCESS;
3342 else if (!rtLockValidatorIsSimpleNoDeadlockCase(pRecU))
3343 rc = rtLockValidatorDeadlockDetection(pRecU, pThreadSelf, pSrcPos);
3344
3345 if (RT_SUCCESS(rc))
3346 ASMAtomicWriteBool(&pThreadSelf->fReallySleeping, fReallySleeping);
3347 else
3348 {
3349 rtThreadSetState(pThreadSelf, enmThreadState);
3350 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pRec, NULL);
3351 }
3352 ASMAtomicWriteBool(&pThreadSelf->LockValidator.fInValidator, false);
3353 return rc;
3354}
3355RT_EXPORT_SYMBOL(RTLockValidatorRecExclCheckBlocking);
3356
3357
3358RTDECL(int) RTLockValidatorRecExclCheckOrderAndBlocking(PRTLOCKVALRECEXCL pRec, RTTHREAD hThreadSelf,
3359 PCRTLOCKVALSRCPOS pSrcPos, bool fRecursiveOk, RTMSINTERVAL cMillies,
3360 RTTHREADSTATE enmSleepState, bool fReallySleeping)
3361{
3362 int rc = RTLockValidatorRecExclCheckOrder(pRec, hThreadSelf, pSrcPos, cMillies);
3363 if (RT_SUCCESS(rc))
3364 rc = RTLockValidatorRecExclCheckBlocking(pRec, hThreadSelf, pSrcPos, fRecursiveOk, cMillies,
3365 enmSleepState, fReallySleeping);
3366 return rc;
3367}
3368RT_EXPORT_SYMBOL(RTLockValidatorRecExclCheckOrderAndBlocking);
3369
3370
3371RTDECL(void) RTLockValidatorRecSharedInitV(PRTLOCKVALRECSHRD pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
3372 void *hLock, bool fSignaller, bool fEnabled, const char *pszNameFmt, va_list va)
3373{
3374 RTLOCKVAL_ASSERT_PTR_ALIGN(pRec);
3375 RTLOCKVAL_ASSERT_PTR_ALIGN(hLock);
3376 Assert( uSubClass >= RTLOCKVAL_SUB_CLASS_USER
3377 || uSubClass == RTLOCKVAL_SUB_CLASS_NONE
3378 || uSubClass == RTLOCKVAL_SUB_CLASS_ANY);
3379
3380 pRec->Core.u32Magic = RTLOCKVALRECSHRD_MAGIC;
3381 pRec->uSubClass = uSubClass;
3382 pRec->hClass = rtLockValidatorClassValidateAndRetain(hClass);
3383 pRec->hLock = hLock;
3384 pRec->fEnabled = fEnabled && RTLockValidatorIsEnabled();
3385 pRec->fSignaller = fSignaller;
3386 pRec->pSibling = NULL;
3387
3388 /* the table */
3389 pRec->cEntries = 0;
3390 pRec->iLastEntry = 0;
3391 pRec->cAllocated = 0;
3392 pRec->fReallocating = false;
3393 pRec->fPadding = false;
3394 pRec->papOwners = NULL;
3395
3396 /* the name */
3397 if (pszNameFmt)
3398 RTStrPrintfV(pRec->szName, sizeof(pRec->szName), pszNameFmt, va);
3399 else
3400 {
3401 static uint32_t volatile s_cAnonymous = 0;
3402 uint32_t i = ASMAtomicIncU32(&s_cAnonymous) - 1;
3403 RTStrPrintf(pRec->szName, sizeof(pRec->szName), "anon-shrd-%u", i);
3404 }
3405}
3406
3407
3408RTDECL(void) RTLockValidatorRecSharedInit(PRTLOCKVALRECSHRD pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
3409 void *hLock, bool fSignaller, bool fEnabled, const char *pszNameFmt, ...)
3410{
3411 va_list va;
3412 va_start(va, pszNameFmt);
3413 RTLockValidatorRecSharedInitV(pRec, hClass, uSubClass, hLock, fSignaller, fEnabled, pszNameFmt, va);
3414 va_end(va);
3415}
3416
3417
3418RTDECL(void) RTLockValidatorRecSharedDelete(PRTLOCKVALRECSHRD pRec)
3419{
3420 Assert(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC);
3421
3422 /*
3423 * Flip it into table realloc mode and take the destruction lock.
3424 */
3425 rtLockValidatorSerializeDestructEnter();
3426 while (!ASMAtomicCmpXchgBool(&pRec->fReallocating, true, false))
3427 {
3428 rtLockValidatorSerializeDestructLeave();
3429
3430 rtLockValidatorSerializeDetectionEnter();
3431 rtLockValidatorSerializeDetectionLeave();
3432
3433 rtLockValidatorSerializeDestructEnter();
3434 }
3435
3436 ASMAtomicWriteU32(&pRec->Core.u32Magic, RTLOCKVALRECSHRD_MAGIC_DEAD);
3437 RTLOCKVALCLASS hClass;
3438 ASMAtomicXchgHandle(&pRec->hClass, NIL_RTLOCKVALCLASS, &hClass);
3439 if (pRec->papOwners)
3440 {
3441 PRTLOCKVALRECSHRDOWN volatile *papOwners = pRec->papOwners;
3442 ASMAtomicUoWritePtr((void * volatile *)&pRec->papOwners, NULL);
3443 ASMAtomicUoWriteU32(&pRec->cAllocated, 0);
3444
3445 RTMemFree((void *)pRec->papOwners);
3446 }
3447 if (pRec->pSibling)
3448 rtLockValidatorUnlinkAllSiblings(&pRec->Core);
3449 ASMAtomicWriteBool(&pRec->fReallocating, false);
3450
3451 rtLockValidatorSerializeDestructLeave();
3452
3453 if (hClass != NIL_RTLOCKVALCLASS)
3454 RTLockValidatorClassRelease(hClass);
3455}
3456
3457
3458RTDECL(uint32_t) RTLockValidatorRecSharedSetSubClass(PRTLOCKVALRECSHRD pRec, uint32_t uSubClass)
3459{
3460 AssertPtrReturn(pRec, RTLOCKVAL_SUB_CLASS_INVALID);
3461 AssertReturn(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
3462 AssertReturn( uSubClass >= RTLOCKVAL_SUB_CLASS_USER
3463 || uSubClass == RTLOCKVAL_SUB_CLASS_NONE
3464 || uSubClass == RTLOCKVAL_SUB_CLASS_ANY,
3465 RTLOCKVAL_SUB_CLASS_INVALID);
3466 return ASMAtomicXchgU32(&pRec->uSubClass, uSubClass);
3467}
3468
3469
3470/**
3471 * Locates an owner (thread) in a shared lock record.
3472 *
3473 * @returns Pointer to the owner entry on success, NULL on failure..
3474 * @param pShared The shared lock record.
3475 * @param hThread The thread (owner) to find.
3476 * @param piEntry Where to optionally return the table in index.
3477 * Optional.
3478 */
3479DECLINLINE(PRTLOCKVALRECUNION)
3480rtLockValidatorRecSharedFindOwner(PRTLOCKVALRECSHRD pShared, RTTHREAD hThread, uint32_t *piEntry)
3481{
3482 rtLockValidatorSerializeDetectionEnter();
3483
3484 PRTLOCKVALRECSHRDOWN volatile *papOwners = pShared->papOwners;
3485 if (papOwners)
3486 {
3487 uint32_t const cMax = pShared->cAllocated;
3488 for (uint32_t iEntry = 0; iEntry < cMax; iEntry++)
3489 {
3490 PRTLOCKVALRECUNION pEntry = (PRTLOCKVALRECUNION)rtLockValidatorUoReadSharedOwner(&papOwners[iEntry]);
3491 if (pEntry && pEntry->ShrdOwner.hThread == hThread)
3492 {
3493 rtLockValidatorSerializeDetectionLeave();
3494 if (piEntry)
3495 *piEntry = iEntry;
3496 return pEntry;
3497 }
3498 }
3499 }
3500
3501 rtLockValidatorSerializeDetectionLeave();
3502 return NULL;
3503}
3504
3505
3506RTDECL(int) RTLockValidatorRecSharedCheckOrder(PRTLOCKVALRECSHRD pRec, RTTHREAD hThreadSelf,
3507 PCRTLOCKVALSRCPOS pSrcPos, RTMSINTERVAL cMillies)
3508{
3509 /*
3510 * Validate and adjust input. Quit early if order validation is disabled.
3511 */
3512 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3513 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3514 if ( !pRecU->Shared.fEnabled
3515 || pRecU->Shared.hClass == NIL_RTLOCKVALCLASS
3516 || pRecU->Shared.hClass->cMsMinOrder == RT_INDEFINITE_WAIT
3517 || pRecU->Shared.hClass->cMsMinOrder > cMillies
3518 )
3519 return VINF_SUCCESS;
3520
3521 if (hThreadSelf == NIL_RTTHREAD)
3522 {
3523 hThreadSelf = RTThreadSelfAutoAdopt();
3524 AssertReturn(hThreadSelf != NIL_RTTHREAD, VERR_SEM_LV_INTERNAL_ERROR);
3525 }
3526 AssertReturn(hThreadSelf->u32Magic == RTTHREADINT_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3527 Assert(hThreadSelf == RTThreadSelf());
3528
3529 /*
3530 * Detect recursion as it isn't subject to order restrictions.
3531 */
3532 PRTLOCKVALRECUNION pEntry = rtLockValidatorRecSharedFindOwner(&pRecU->Shared, hThreadSelf, NULL);
3533 if (pEntry)
3534 return VINF_SUCCESS;
3535
3536 return rtLockValidatorStackCheckLockingOrder(pRecU->Shared.hClass, pRecU->Shared.uSubClass, hThreadSelf, pRecU, pSrcPos);
3537}
3538
3539
3540RTDECL(int) RTLockValidatorRecSharedCheckBlocking(PRTLOCKVALRECSHRD pRec, RTTHREAD hThreadSelf,
3541 PCRTLOCKVALSRCPOS pSrcPos, bool fRecursiveOk, RTMSINTERVAL cMillies,
3542 RTTHREADSTATE enmSleepState, bool fReallySleeping)
3543{
3544 /*
3545 * Fend off wild life.
3546 */
3547 PRTLOCKVALRECUNION pRecU = (PRTLOCKVALRECUNION)pRec;
3548 AssertPtrReturn(pRecU, VERR_SEM_LV_INVALID_PARAMETER);
3549 AssertReturn(pRecU->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3550 if (!pRecU->Shared.fEnabled)
3551 return VINF_SUCCESS;
3552
3553 PRTTHREADINT pThreadSelf = hThreadSelf;
3554 AssertPtrReturn(pThreadSelf, VERR_SEM_LV_INVALID_PARAMETER);
3555 AssertReturn(pThreadSelf->u32Magic == RTTHREADINT_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
3556 Assert(pThreadSelf == RTThreadSelf());
3557
3558 AssertReturn(RTTHREAD_IS_SLEEPING(enmSleepState), VERR_SEM_LV_INVALID_PARAMETER);
3559
3560 RTTHREADSTATE enmThreadState = rtThreadGetState(pThreadSelf);
3561 if (RT_UNLIKELY(enmThreadState != RTTHREADSTATE_RUNNING))
3562 {
3563 AssertReturn( enmThreadState == RTTHREADSTATE_TERMINATED /* rtThreadRemove uses locks too */
3564 || enmThreadState == RTTHREADSTATE_INITIALIZING /* rtThreadInsert uses locks too */
3565 , VERR_SEM_LV_INVALID_PARAMETER);
3566 enmSleepState = enmThreadState;
3567 }
3568
3569 /*
3570 * Record the location.
3571 */
3572 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pRec, pRecU);
3573 rtLockValidatorSrcPosCopy(&pThreadSelf->LockValidator.SrcPos, pSrcPos);
3574 ASMAtomicWriteBool(&pThreadSelf->LockValidator.fInValidator, true);
3575 pThreadSelf->LockValidator.enmRecState = enmSleepState;
3576 rtThreadSetState(pThreadSelf, enmSleepState);
3577
3578 /*
3579 * Don't do deadlock detection if we're recursing.
3580 */
3581 int rc = VINF_SUCCESS;
3582 PRTLOCKVALRECUNION pEntry = !pRecU->Shared.fSignaller
3583 ? rtLockValidatorRecSharedFindOwner(&pRecU->Shared, pThreadSelf, NULL)
3584 : NULL;
3585 if (pEntry)
3586 {
3587 if ( !fRecursiveOk
3588 || ( pRec->hClass
3589 && !pRec->hClass->fRecursionOk)
3590 )
3591 {
3592 rtLockValComplainFirst("Recursion not allowed!", pSrcPos, pThreadSelf, pRecU, true);
3593 rtLockValComplainPanic();
3594 rc = VERR_SEM_LV_NESTED;
3595 }
3596 }
3597 /*
3598 * Perform deadlock detection.
3599 */
3600 else if ( pRec->hClass
3601 && ( pRec->hClass->cMsMinDeadlock == RT_INDEFINITE_WAIT
3602 || pRec->hClass->cMsMinDeadlock > cMillies))
3603 rc = VINF_SUCCESS;
3604 else if (!rtLockValidatorIsSimpleNoDeadlockCase(pRecU))
3605 rc = rtLockValidatorDeadlockDetection(pRecU, pThreadSelf, pSrcPos);
3606
3607 if (RT_SUCCESS(rc))
3608 ASMAtomicWriteBool(&pThreadSelf->fReallySleeping, fReallySleeping);
3609 else
3610 {
3611 rtThreadSetState(pThreadSelf, enmThreadState);
3612 rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pRec, NULL);
3613 }
3614 ASMAtomicWriteBool(&pThreadSelf->LockValidator.fInValidator, false);
3615 return rc;
3616}
3617RT_EXPORT_SYMBOL(RTLockValidatorRecSharedCheckBlocking);
3618
3619
3620RTDECL(int) RTLockValidatorRecSharedCheckOrderAndBlocking(PRTLOCKVALRECSHRD pRec, RTTHREAD hThreadSelf,
3621 PCRTLOCKVALSRCPOS pSrcPos, bool fRecursiveOk, RTMSINTERVAL cMillies,
3622 RTTHREADSTATE enmSleepState, bool fReallySleeping)
3623{
3624 int rc = RTLockValidatorRecSharedCheckOrder(pRec, hThreadSelf, pSrcPos, cMillies);
3625 if (RT_SUCCESS(rc))
3626 rc = RTLockValidatorRecSharedCheckBlocking(pRec, hThreadSelf, pSrcPos, fRecursiveOk, cMillies,
3627 enmSleepState, fReallySleeping);
3628 return rc;
3629}
3630RT_EXPORT_SYMBOL(RTLockValidatorRecSharedCheckOrderAndBlocking);
3631
3632
3633/**
3634 * Allocates and initializes an owner entry for the shared lock record.
3635 *
3636 * @returns The new owner entry.
3637 * @param pRec The shared lock record.
3638 * @param pThreadSelf The calling thread and owner. Used for record
3639 * initialization and allocation.
3640 * @param pSrcPos The source position.
3641 */
3642DECLINLINE(PRTLOCKVALRECUNION)
3643rtLockValidatorRecSharedAllocOwner(PRTLOCKVALRECSHRD pRec, PRTTHREADINT pThreadSelf, PCRTLOCKVALSRCPOS pSrcPos)
3644{
3645 PRTLOCKVALRECUNION pEntry;
3646
3647 /*
3648 * Check if the thread has any statically allocated records we can easily
3649 * make use of.
3650 */
3651 unsigned iEntry = ASMBitFirstSetU32(ASMAtomicUoReadU32(&pThreadSelf->LockValidator.bmFreeShrdOwners));
3652 if ( iEntry > 0
3653 && ASMAtomicBitTestAndClear(&pThreadSelf->LockValidator.bmFreeShrdOwners, iEntry - 1))
3654 {
3655 pEntry = (PRTLOCKVALRECUNION)&pThreadSelf->LockValidator.aShrdOwners[iEntry - 1];
3656 Assert(!pEntry->ShrdOwner.fReserved);
3657 pEntry->ShrdOwner.fStaticAlloc = true;
3658 rtThreadGet(pThreadSelf);
3659 }
3660 else
3661 {
3662 pEntry = (PRTLOCKVALRECUNION)RTMemAlloc(sizeof(RTLOCKVALRECSHRDOWN));
3663 if (RT_UNLIKELY(!pEntry))
3664 return NULL;
3665 pEntry->ShrdOwner.fStaticAlloc = false;
3666 }
3667
3668 pEntry->Core.u32Magic = RTLOCKVALRECSHRDOWN_MAGIC;
3669 pEntry->ShrdOwner.cRecursion = 1;
3670 pEntry->ShrdOwner.fReserved = true;
3671 pEntry->ShrdOwner.hThread = pThreadSelf;
3672 pEntry->ShrdOwner.pDown = NULL;
3673 pEntry->ShrdOwner.pSharedRec = pRec;
3674#if HC_ARCH_BITS == 32
3675 pEntry->ShrdOwner.pvReserved = NULL;
3676#endif
3677 if (pSrcPos)
3678 pEntry->ShrdOwner.SrcPos = *pSrcPos;
3679 else
3680 rtLockValidatorSrcPosInit(&pEntry->ShrdOwner.SrcPos);
3681 return pEntry;
3682}
3683
3684
3685/**
3686 * Frees an owner entry allocated by rtLockValidatorRecSharedAllocOwner.
3687 *
3688 * @param pEntry The owner entry.
3689 */
3690DECLINLINE(void) rtLockValidatorRecSharedFreeOwner(PRTLOCKVALRECSHRDOWN pEntry)
3691{
3692 if (pEntry)
3693 {
3694 Assert(pEntry->Core.u32Magic == RTLOCKVALRECSHRDOWN_MAGIC);
3695 ASMAtomicWriteU32(&pEntry->Core.u32Magic, RTLOCKVALRECSHRDOWN_MAGIC_DEAD);
3696
3697 PRTTHREADINT pThread;
3698 ASMAtomicXchgHandle(&pEntry->hThread, NIL_RTTHREAD, &pThread);
3699
3700 Assert(pEntry->fReserved);
3701 pEntry->fReserved = false;
3702
3703 if (pEntry->fStaticAlloc)
3704 {
3705 AssertPtrReturnVoid(pThread);
3706 AssertReturnVoid(pThread->u32Magic == RTTHREADINT_MAGIC);
3707
3708 uintptr_t iEntry = pEntry - &pThread->LockValidator.aShrdOwners[0];
3709 AssertReleaseReturnVoid(iEntry < RT_ELEMENTS(pThread->LockValidator.aShrdOwners));
3710
3711 Assert(!ASMBitTest(&pThread->LockValidator.bmFreeShrdOwners, iEntry));
3712 ASMAtomicBitSet(&pThread->LockValidator.bmFreeShrdOwners, iEntry);
3713
3714 rtThreadRelease(pThread);
3715 }
3716 else
3717 {
3718 rtLockValidatorSerializeDestructEnter();
3719 rtLockValidatorSerializeDestructLeave();
3720
3721 RTMemFree(pEntry);
3722 }
3723 }
3724}
3725
3726
3727/**
3728 * Make more room in the table.
3729 *
3730 * @retval true on success
3731 * @retval false if we're out of memory or running into a bad race condition
3732 * (probably a bug somewhere). No longer holding the lock.
3733 *
3734 * @param pShared The shared lock record.
3735 */
3736static bool rtLockValidatorRecSharedMakeRoom(PRTLOCKVALRECSHRD pShared)
3737{
3738 for (unsigned i = 0; i < 1000; i++)
3739 {
3740 /*
3741 * Switch to the other data access direction.
3742 */
3743 rtLockValidatorSerializeDetectionLeave();
3744 if (i >= 10)
3745 {
3746 Assert(i != 10 && i != 100);
3747 RTThreadSleep(i >= 100);
3748 }
3749 rtLockValidatorSerializeDestructEnter();
3750
3751 /*
3752 * Try grab the privilege to reallocating the table.
3753 */
3754 if ( pShared->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC
3755 && ASMAtomicCmpXchgBool(&pShared->fReallocating, true, false))
3756 {
3757 uint32_t cAllocated = pShared->cAllocated;
3758 if (cAllocated < pShared->cEntries)
3759 {
3760 /*
3761 * Ok, still not enough space. Reallocate the table.
3762 */
3763#if 0 /** @todo enable this after making sure growing works flawlessly. */
3764 uint32_t cInc = RT_ALIGN_32(pShared->cEntries - cAllocated, 16);
3765#else
3766 uint32_t cInc = RT_ALIGN_32(pShared->cEntries - cAllocated, 1);
3767#endif
3768 PRTLOCKVALRECSHRDOWN *papOwners;
3769 papOwners = (PRTLOCKVALRECSHRDOWN *)RTMemRealloc((void *)pShared->papOwners,
3770 (cAllocated + cInc) * sizeof(void *));
3771 if (!papOwners)
3772 {
3773 ASMAtomicWriteBool(&pShared->fReallocating, false);
3774 rtLockValidatorSerializeDestructLeave();
3775 /* RTMemRealloc will assert */
3776 return false;
3777 }
3778
3779 while (cInc-- > 0)
3780 {
3781 papOwners[cAllocated] = NULL;
3782 cAllocated++;
3783 }
3784
3785 ASMAtomicWritePtr((void * volatile *)&pShared->papOwners, papOwners);
3786 ASMAtomicWriteU32(&pShared->cAllocated, cAllocated);
3787 }
3788 ASMAtomicWriteBool(&pShared->fReallocating, false);
3789 }
3790 rtLockValidatorSerializeDestructLeave();
3791
3792 rtLockValidatorSerializeDetectionEnter();
3793 if (RT_UNLIKELY(pShared->Core.u32Magic != RTLOCKVALRECSHRD_MAGIC))
3794 break;
3795
3796 if (pShared->cAllocated >= pShared->cEntries)
3797 return true;
3798 }
3799
3800 rtLockValidatorSerializeDetectionLeave();
3801 AssertFailed(); /* too many iterations or destroyed while racing. */
3802 return false;
3803}
3804
3805
3806/**
3807 * Adds an owner entry to a shared lock record.
3808 *
3809 * @returns true on success, false on serious race or we're if out of memory.
3810 * @param pShared The shared lock record.
3811 * @param pEntry The owner entry.
3812 */
3813DECLINLINE(bool) rtLockValidatorRecSharedAddOwner(PRTLOCKVALRECSHRD pShared, PRTLOCKVALRECSHRDOWN pEntry)
3814{
3815 rtLockValidatorSerializeDetectionEnter();
3816 if (RT_LIKELY(pShared->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC)) /* paranoia */
3817 {
3818 if ( ASMAtomicIncU32(&pShared->cEntries) > pShared->cAllocated /** @todo add fudge */
3819 && !rtLockValidatorRecSharedMakeRoom(pShared))
3820 return false; /* the worker leave the lock */
3821
3822 PRTLOCKVALRECSHRDOWN volatile *papOwners = pShared->papOwners;
3823 uint32_t const cMax = pShared->cAllocated;
3824 for (unsigned i = 0; i < 100; i++)
3825 {
3826 for (uint32_t iEntry = 0; iEntry < cMax; iEntry++)
3827 {
3828 if (ASMAtomicCmpXchgPtr((void * volatile *)&papOwners[iEntry], pEntry, NULL))
3829 {
3830 rtLockValidatorSerializeDetectionLeave();
3831 return true;
3832 }
3833 }
3834 Assert(i != 25);
3835 }
3836 AssertFailed();
3837 }
3838 rtLockValidatorSerializeDetectionLeave();
3839 return false;
3840}
3841
3842
3843/**
3844 * Remove an owner entry from a shared lock record and free it.
3845 *
3846 * @param pShared The shared lock record.
3847 * @param pEntry The owner entry to remove.
3848 * @param iEntry The last known index.
3849 */
3850DECLINLINE(void) rtLockValidatorRecSharedRemoveAndFreeOwner(PRTLOCKVALRECSHRD pShared, PRTLOCKVALRECSHRDOWN pEntry,
3851 uint32_t iEntry)
3852{
3853 /*
3854 * Remove it from the table.
3855 */
3856 rtLockValidatorSerializeDetectionEnter();
3857 AssertReturnVoidStmt(pShared->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, rtLockValidatorSerializeDetectionLeave());
3858 if (RT_UNLIKELY( iEntry >= pShared->cAllocated
3859 || !ASMAtomicCmpXchgPtr((void * volatile *)&pShared->papOwners[iEntry], NULL, pEntry)))
3860 {
3861 /* this shouldn't happen yet... */
3862 AssertFailed();
3863 PRTLOCKVALRECSHRDOWN volatile *papOwners = pShared->papOwners;
3864 uint32_t const cMax = pShared->cAllocated;
3865 for (iEntry = 0; iEntry < cMax; iEntry++)
3866 if (ASMAtomicCmpXchgPtr((void * volatile *)&papOwners[iEntry], NULL, pEntry))
3867 break;
3868 AssertReturnVoidStmt(iEntry < cMax, rtLockValidatorSerializeDetectionLeave());
3869 }
3870 uint32_t cNow = ASMAtomicDecU32(&pShared->cEntries);
3871 Assert(!(cNow & RT_BIT_32(31))); NOREF(cNow);
3872 rtLockValidatorSerializeDetectionLeave();
3873
3874 /*
3875 * Successfully removed, now free it.
3876 */
3877 rtLockValidatorRecSharedFreeOwner(pEntry);
3878}
3879
3880
3881RTDECL(void) RTLockValidatorRecSharedResetOwner(PRTLOCKVALRECSHRD pRec, RTTHREAD hThread, PCRTLOCKVALSRCPOS pSrcPos)
3882{
3883 AssertReturnVoid(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC);
3884 if (!pRec->fEnabled)
3885 return;
3886 AssertReturnVoid(hThread == NIL_RTTHREAD || hThread->u32Magic == RTTHREADINT_MAGIC);
3887 AssertReturnVoid(pRec->fSignaller);
3888
3889 /*
3890 * Free all current owners.
3891 */
3892 rtLockValidatorSerializeDetectionEnter();
3893 while (ASMAtomicUoReadU32(&pRec->cEntries) > 0)
3894 {
3895 AssertReturnVoidStmt(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, rtLockValidatorSerializeDetectionLeave());
3896 uint32_t iEntry = 0;
3897 uint32_t cEntries = pRec->cAllocated;
3898 PRTLOCKVALRECSHRDOWN volatile *papEntries = pRec->papOwners;
3899 while (iEntry < cEntries)
3900 {
3901 PRTLOCKVALRECSHRDOWN pEntry = (PRTLOCKVALRECSHRDOWN)ASMAtomicXchgPtr((void * volatile *)&papEntries[iEntry], NULL);
3902 if (pEntry)
3903 {
3904 ASMAtomicDecU32(&pRec->cEntries);
3905 rtLockValidatorSerializeDetectionLeave();
3906
3907 rtLockValidatorRecSharedFreeOwner(pEntry);
3908
3909 rtLockValidatorSerializeDetectionEnter();
3910 if (ASMAtomicUoReadU32(&pRec->cEntries) == 0)
3911 break;
3912 cEntries = pRec->cAllocated;
3913 papEntries = pRec->papOwners;
3914 }
3915 iEntry++;
3916 }
3917 }
3918 rtLockValidatorSerializeDetectionLeave();
3919
3920 if (hThread != NIL_RTTHREAD)
3921 {
3922 /*
3923 * Allocate a new owner entry and insert it into the table.
3924 */
3925 PRTLOCKVALRECUNION pEntry = rtLockValidatorRecSharedAllocOwner(pRec, hThread, pSrcPos);
3926 if ( pEntry
3927 && !rtLockValidatorRecSharedAddOwner(pRec, &pEntry->ShrdOwner))
3928 rtLockValidatorRecSharedFreeOwner(&pEntry->ShrdOwner);
3929 }
3930}
3931RT_EXPORT_SYMBOL(RTLockValidatorRecSharedResetOwner);
3932
3933
3934RTDECL(void) RTLockValidatorRecSharedAddOwner(PRTLOCKVALRECSHRD pRec, RTTHREAD hThread, PCRTLOCKVALSRCPOS pSrcPos)
3935{
3936 AssertReturnVoid(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC);
3937 if (!pRec->fEnabled)
3938 return;
3939 if (hThread == NIL_RTTHREAD)
3940 {
3941 hThread = RTThreadSelfAutoAdopt();
3942 AssertReturnVoid(hThread != NIL_RTTHREAD);
3943 }
3944 AssertReturnVoid(hThread->u32Magic == RTTHREADINT_MAGIC);
3945
3946 /*
3947 * Recursive?
3948 *
3949 * Note! This code can be optimized to try avoid scanning the table on
3950 * insert. However, that's annoying work that makes the code big,
3951 * so it can wait til later sometime.
3952 */
3953 PRTLOCKVALRECUNION pEntry = rtLockValidatorRecSharedFindOwner(pRec, hThread, NULL);
3954 if (pEntry)
3955 {
3956 Assert(!pRec->fSignaller);
3957 pEntry->ShrdOwner.cRecursion++;
3958 rtLockValidatorStackPushRecursion(hThread, pEntry, pSrcPos);
3959 return;
3960 }
3961
3962 /*
3963 * Allocate a new owner entry and insert it into the table.
3964 */
3965 pEntry = rtLockValidatorRecSharedAllocOwner(pRec, hThread, pSrcPos);
3966 if (pEntry)
3967 {
3968 if (rtLockValidatorRecSharedAddOwner(pRec, &pEntry->ShrdOwner))
3969 {
3970 if (!pRec->fSignaller)
3971 rtLockValidatorStackPush(hThread, pEntry);
3972 }
3973 else
3974 rtLockValidatorRecSharedFreeOwner(&pEntry->ShrdOwner);
3975 }
3976}
3977RT_EXPORT_SYMBOL(RTLockValidatorRecSharedAddOwner);
3978
3979
3980RTDECL(void) RTLockValidatorRecSharedRemoveOwner(PRTLOCKVALRECSHRD pRec, RTTHREAD hThread)
3981{
3982 AssertReturnVoid(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC);
3983 if (!pRec->fEnabled)
3984 return;
3985 AssertReturnVoid(hThread != NIL_RTTHREAD);
3986 AssertReturnVoid(hThread->u32Magic == RTTHREADINT_MAGIC);
3987
3988 /*
3989 * Find the entry hope it's a recursive one.
3990 */
3991 uint32_t iEntry = UINT32_MAX; /* shuts up gcc */
3992 PRTLOCKVALRECUNION pEntry = rtLockValidatorRecSharedFindOwner(pRec, hThread, &iEntry);
3993 AssertReturnVoid(pEntry);
3994 AssertReturnVoid(pEntry->ShrdOwner.cRecursion > 0);
3995
3996 uint32_t c = --pEntry->ShrdOwner.cRecursion;
3997 if (c == 0)
3998 {
3999 if (!pRec->fSignaller)
4000 rtLockValidatorStackPop(hThread, (PRTLOCKVALRECUNION)pEntry);
4001 rtLockValidatorRecSharedRemoveAndFreeOwner(pRec, &pEntry->ShrdOwner, iEntry);
4002 }
4003 else
4004 {
4005 Assert(!pRec->fSignaller);
4006 rtLockValidatorStackPopRecursion(hThread, pEntry);
4007 }
4008}
4009RT_EXPORT_SYMBOL(RTLockValidatorRecSharedRemoveOwner);
4010
4011
4012RTDECL(int) RTLockValidatorRecSharedCheckAndRelease(PRTLOCKVALRECSHRD pRec, RTTHREAD hThreadSelf)
4013{
4014 AssertReturn(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
4015 if (!pRec->fEnabled)
4016 return VINF_SUCCESS;
4017 if (hThreadSelf == NIL_RTTHREAD)
4018 {
4019 hThreadSelf = RTThreadSelfAutoAdopt();
4020 AssertReturn(hThreadSelf != NIL_RTTHREAD, VERR_SEM_LV_INTERNAL_ERROR);
4021 }
4022 Assert(hThreadSelf == RTThreadSelf());
4023 AssertReturn(hThreadSelf->u32Magic == RTTHREADINT_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
4024
4025 /*
4026 * Locate the entry for this thread in the table.
4027 */
4028 uint32_t iEntry = 0;
4029 PRTLOCKVALRECUNION pEntry = rtLockValidatorRecSharedFindOwner(pRec, hThreadSelf, &iEntry);
4030 if (RT_UNLIKELY(!pEntry))
4031 {
4032 rtLockValComplainFirst("Not owner (shared)!", NULL, hThreadSelf, (PRTLOCKVALRECUNION)pRec, true);
4033 rtLockValComplainPanic();
4034 return VERR_SEM_LV_NOT_OWNER;
4035 }
4036
4037 /*
4038 * Check the release order.
4039 */
4040 if ( pRec->hClass != NIL_RTLOCKVALCLASS
4041 && pRec->hClass->fStrictReleaseOrder
4042 && pRec->hClass->cMsMinOrder != RT_INDEFINITE_WAIT
4043 )
4044 {
4045 int rc = rtLockValidatorStackCheckReleaseOrder(hThreadSelf, (PRTLOCKVALRECUNION)pEntry);
4046 if (RT_FAILURE(rc))
4047 return rc;
4048 }
4049
4050 /*
4051 * Release the ownership or unwind a level of recursion.
4052 */
4053 Assert(pEntry->ShrdOwner.cRecursion > 0);
4054 uint32_t c = --pEntry->ShrdOwner.cRecursion;
4055 if (c == 0)
4056 {
4057 rtLockValidatorStackPop(hThreadSelf, pEntry);
4058 rtLockValidatorRecSharedRemoveAndFreeOwner(pRec, &pEntry->ShrdOwner, iEntry);
4059 }
4060 else
4061 rtLockValidatorStackPopRecursion(hThreadSelf, pEntry);
4062
4063 return VINF_SUCCESS;
4064}
4065
4066
4067RTDECL(int) RTLockValidatorRecSharedCheckSignaller(PRTLOCKVALRECSHRD pRec, RTTHREAD hThreadSelf)
4068{
4069 AssertReturn(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
4070 if (!pRec->fEnabled)
4071 return VINF_SUCCESS;
4072 if (hThreadSelf == NIL_RTTHREAD)
4073 {
4074 hThreadSelf = RTThreadSelfAutoAdopt();
4075 AssertReturn(hThreadSelf != NIL_RTTHREAD, VERR_SEM_LV_INTERNAL_ERROR);
4076 }
4077 Assert(hThreadSelf == RTThreadSelf());
4078 AssertReturn(hThreadSelf->u32Magic == RTTHREADINT_MAGIC, VERR_SEM_LV_INVALID_PARAMETER);
4079
4080 /*
4081 * Locate the entry for this thread in the table.
4082 */
4083 uint32_t iEntry = 0;
4084 PRTLOCKVALRECUNION pEntry = rtLockValidatorRecSharedFindOwner(pRec, hThreadSelf, &iEntry);
4085 if (RT_UNLIKELY(!pEntry))
4086 {
4087 rtLockValComplainFirst("Invalid signaller!", NULL, hThreadSelf, (PRTLOCKVALRECUNION)pRec, true);
4088 rtLockValComplainPanic();
4089 return VERR_SEM_LV_NOT_SIGNALLER;
4090 }
4091 return VINF_SUCCESS;
4092}
4093
4094
4095RTDECL(int32_t) RTLockValidatorWriteLockGetCount(RTTHREAD Thread)
4096{
4097 if (Thread == NIL_RTTHREAD)
4098 return 0;
4099
4100 PRTTHREADINT pThread = rtThreadGet(Thread);
4101 if (!pThread)
4102 return VERR_INVALID_HANDLE;
4103 int32_t cWriteLocks = ASMAtomicReadS32(&pThread->LockValidator.cWriteLocks);
4104 rtThreadRelease(pThread);
4105 return cWriteLocks;
4106}
4107RT_EXPORT_SYMBOL(RTLockValidatorWriteLockGetCount);
4108
4109
4110RTDECL(void) RTLockValidatorWriteLockInc(RTTHREAD Thread)
4111{
4112 PRTTHREADINT pThread = rtThreadGet(Thread);
4113 AssertReturnVoid(pThread);
4114 ASMAtomicIncS32(&pThread->LockValidator.cWriteLocks);
4115 rtThreadRelease(pThread);
4116}
4117RT_EXPORT_SYMBOL(RTLockValidatorWriteLockInc);
4118
4119
4120RTDECL(void) RTLockValidatorWriteLockDec(RTTHREAD Thread)
4121{
4122 PRTTHREADINT pThread = rtThreadGet(Thread);
4123 AssertReturnVoid(pThread);
4124 ASMAtomicDecS32(&pThread->LockValidator.cWriteLocks);
4125 rtThreadRelease(pThread);
4126}
4127RT_EXPORT_SYMBOL(RTLockValidatorWriteLockDec);
4128
4129
4130RTDECL(int32_t) RTLockValidatorReadLockGetCount(RTTHREAD Thread)
4131{
4132 if (Thread == NIL_RTTHREAD)
4133 return 0;
4134
4135 PRTTHREADINT pThread = rtThreadGet(Thread);
4136 if (!pThread)
4137 return VERR_INVALID_HANDLE;
4138 int32_t cReadLocks = ASMAtomicReadS32(&pThread->LockValidator.cReadLocks);
4139 rtThreadRelease(pThread);
4140 return cReadLocks;
4141}
4142RT_EXPORT_SYMBOL(RTLockValidatorReadLockGetCount);
4143
4144
4145RTDECL(void) RTLockValidatorReadLockInc(RTTHREAD Thread)
4146{
4147 PRTTHREADINT pThread = rtThreadGet(Thread);
4148 Assert(pThread);
4149 ASMAtomicIncS32(&pThread->LockValidator.cReadLocks);
4150 rtThreadRelease(pThread);
4151}
4152RT_EXPORT_SYMBOL(RTLockValidatorReadLockInc);
4153
4154
4155RTDECL(void) RTLockValidatorReadLockDec(RTTHREAD Thread)
4156{
4157 PRTTHREADINT pThread = rtThreadGet(Thread);
4158 Assert(pThread);
4159 ASMAtomicDecS32(&pThread->LockValidator.cReadLocks);
4160 rtThreadRelease(pThread);
4161}
4162RT_EXPORT_SYMBOL(RTLockValidatorReadLockDec);
4163
4164
4165RTDECL(void *) RTLockValidatorQueryBlocking(RTTHREAD hThread)
4166{
4167 void *pvLock = NULL;
4168 PRTTHREADINT pThread = rtThreadGet(hThread);
4169 if (pThread)
4170 {
4171 RTTHREADSTATE enmState = rtThreadGetState(pThread);
4172 if (RTTHREAD_IS_SLEEPING(enmState))
4173 {
4174 rtLockValidatorSerializeDetectionEnter();
4175
4176 enmState = rtThreadGetState(pThread);
4177 if (RTTHREAD_IS_SLEEPING(enmState))
4178 {
4179 PRTLOCKVALRECUNION pRec = rtLockValidatorReadRecUnionPtr(&pThread->LockValidator.pRec);
4180 if (pRec)
4181 {
4182 switch (pRec->Core.u32Magic)
4183 {
4184 case RTLOCKVALRECEXCL_MAGIC:
4185 pvLock = pRec->Excl.hLock;
4186 break;
4187
4188 case RTLOCKVALRECSHRDOWN_MAGIC:
4189 pRec = (PRTLOCKVALRECUNION)pRec->ShrdOwner.pSharedRec;
4190 if (!pRec || pRec->Core.u32Magic != RTLOCKVALRECSHRD_MAGIC)
4191 break;
4192 case RTLOCKVALRECSHRD_MAGIC:
4193 pvLock = pRec->Shared.hLock;
4194 break;
4195 }
4196 if (RTThreadGetState(pThread) != enmState)
4197 pvLock = NULL;
4198 }
4199 }
4200
4201 rtLockValidatorSerializeDetectionLeave();
4202 }
4203 rtThreadRelease(pThread);
4204 }
4205 return pvLock;
4206}
4207RT_EXPORT_SYMBOL(RTLockValidatorQueryBlocking);
4208
4209
4210RTDECL(bool) RTLockValidatorIsBlockedThreadInValidator(RTTHREAD hThread)
4211{
4212 bool fRet = false;
4213 PRTTHREADINT pThread = rtThreadGet(hThread);
4214 if (pThread)
4215 {
4216 fRet = ASMAtomicReadBool(&pThread->LockValidator.fInValidator);
4217 rtThreadRelease(pThread);
4218 }
4219 return fRet;
4220}
4221RT_EXPORT_SYMBOL(RTLockValidatorIsBlockedThreadInValidator);
4222
4223
4224RTDECL(bool) RTLockValidatorSetEnabled(bool fEnabled)
4225{
4226 return ASMAtomicXchgBool(&g_fLockValidatorEnabled, fEnabled);
4227}
4228RT_EXPORT_SYMBOL(RTLockValidatorSetEnabled);
4229
4230
4231RTDECL(bool) RTLockValidatorIsEnabled(void)
4232{
4233 return ASMAtomicUoReadBool(&g_fLockValidatorEnabled);
4234}
4235RT_EXPORT_SYMBOL(RTLockValidatorIsEnabled);
4236
4237
4238RTDECL(bool) RTLockValidatorSetQuiet(bool fQuiet)
4239{
4240 return ASMAtomicXchgBool(&g_fLockValidatorQuiet, fQuiet);
4241}
4242RT_EXPORT_SYMBOL(RTLockValidatorSetQuiet);
4243
4244
4245RTDECL(bool) RTLockValidatorIsQuiet(void)
4246{
4247 return ASMAtomicUoReadBool(&g_fLockValidatorQuiet);
4248}
4249RT_EXPORT_SYMBOL(RTLockValidatorIsQuiet);
4250
4251
4252RTDECL(bool) RTLockValidatorSetMayPanic(bool fMayPanic)
4253{
4254 return ASMAtomicXchgBool(&g_fLockValidatorMayPanic, fMayPanic);
4255}
4256RT_EXPORT_SYMBOL(RTLockValidatorSetMayPanic);
4257
4258
4259RTDECL(bool) RTLockValidatorMayPanic(void)
4260{
4261 return ASMAtomicUoReadBool(&g_fLockValidatorMayPanic);
4262}
4263RT_EXPORT_SYMBOL(RTLockValidatorMayPanic);
4264
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