lockvalidator.cpp revision 9dd2042711d2a692fa40bd2f7105693a7dc32329
/* $Id$ */
/** @file
* IPRT - Lock Validator.
*/
/*
* Copyright (C) 2009-2010 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/lockvalidator.h>
#include <iprt/semaphore.h>
#include "internal/lockvalidator.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Macro that asserts that a pointer is aligned correctly.
* Only used when fighting bugs. */
#if 1
# define RTLOCKVAL_ASSERT_PTR_ALIGN(p) \
#else
# define RTLOCKVAL_ASSERT_PTR_ALIGN(p) do { } while (0)
#endif
/** Hashes the class handle (pointer) into an apPriorLocksHash index. */
#define RTLOCKVALCLASS_HASH(hClass) \
/ sizeof(PRTLOCKVALCLASSREF)) )
/** The max value for RTLOCKVALCLASSINT::cRefs. */
/** The max value for RTLOCKVALCLASSREF::cLookups. */
/** The absolute max value for RTLOCKVALCLASSREF::cLookups at which it will
* be set back to RTLOCKVALCLASSREF_MAX_LOOKUPS. */
/** @def RTLOCKVAL_WITH_RECURSION_RECORDS
* Enable recursion records. */
#if defined(IN_RING3) || defined(DOXYGEN_RUNNING)
# define RTLOCKVAL_WITH_RECURSION_RECORDS 1
#endif
/** @def RTLOCKVAL_WITH_VERBOSE_DUMPS
* Enables some extra verbosity in the lock dumping. */
#if defined(DOXYGEN_RUNNING)
# define RTLOCKVAL_WITH_VERBOSE_DUMPS
#endif
/** @def RTLOCKVAL_WITH_CLASS_HASH_STATS
* Enables collection prior class hash lookup statistics, dumping them when
* complaining about the class. */
#if defined(DEBUG) || defined(DOXYGEN_RUNNING)
# define RTLOCKVAL_WITH_CLASS_HASH_STATS
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Deadlock detection stack entry.
*/
typedef struct RTLOCKVALDDENTRY
{
/** The current record. */
/** The current entry number if pRec is a shared one. */
/** The thread state of the thread we followed to get to pFirstSibling.
* This is only used for validating a deadlock stack. */
/** The thread we followed to get to pFirstSibling.
* This is only used for validating a deadlock stack. */
/** What pThread is waiting on, i.e. where we entered the circular list of
* siblings. This is used for validating a deadlock stack as well as
* terminating the sibling walk. */
/**
* Deadlock detection stack.
*/
typedef struct RTLOCKVALDDSTACK
{
/** The number stack entries. */
uint32_t c;
/** The stack entries. */
RTLOCKVALDDENTRY a[32];
/** Pointer to a deadlock detction stack. */
typedef RTLOCKVALDDSTACK *PRTLOCKVALDDSTACK;
/**
* Reference to another class.
*/
typedef struct RTLOCKVALCLASSREF
{
/** The class. */
/** The number of lookups of this class. */
/** Indicates whether the entry was added automatically during order checking
* (true) or manually via the API (false). */
bool fAutodidacticism;
/** Reserved / explicit alignment padding. */
bool afReserved[3];
/** Pointer to a class reference. */
typedef RTLOCKVALCLASSREF *PRTLOCKVALCLASSREF;
/** Pointer to a chunk of class references. */
typedef struct RTLOCKVALCLASSREFCHUNK *PRTLOCKVALCLASSREFCHUNK;
/**
* Chunk of class references.
*/
typedef struct RTLOCKVALCLASSREFCHUNK
{
/** Array of refs. */
#if 0 /** @todo for testing alloction of new chunks. */
#else
#endif
/** Pointer to the next chunk. */
PRTLOCKVALCLASSREFCHUNK volatile pNext;
/**
* Lock class.
*/
typedef struct RTLOCKVALCLASSINT
{
/** AVL node core. */
/** Magic value (RTLOCKVALCLASS_MAGIC). */
/** Reference counter. See RTLOCKVALCLASS_MAX_REFS. */
/** Whether the class is allowed to teach it self new locking order rules. */
bool fAutodidact;
/** Whether to allow recursion. */
bool fRecursionOk;
/** Strict release order. */
bool fStrictReleaseOrder;
/** Whether this class is in the tree. */
bool fInTree;
/** Donate a reference to the next retainer. This is a hack to make
* RTLockValidatorClassCreateUnique work. */
bool volatile fDonateRefToNextRetainer;
/** Reserved future use / explicit alignment. */
bool afReserved[3];
/** The minimum wait interval for which we do deadlock detection
* (milliseconds). */
/** The minimum wait interval for which we do order checks (milliseconds). */
/** More padding. */
/** Classes that may be taken prior to this one.
* This is a linked list where each node contains a chunk of locks so that we
* reduce the number of allocations as well as localize the data. */
/** Hash table containing frequently encountered prior locks. */
/** Class name. (Allocated after the end of the block as usual.) */
char const *pszName;
/** Where this class was created.
* This is mainly used for finding automatically created lock classes.
* @remarks The strings are stored after this structure so we won't crash
* spawned it. */
/** Hash hits. */
/** Hash misses. */
uint32_t volatile cHashMisses;
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Serializing object destruction and deadlock detection.
*
* This makes sure that none of the memory examined by the deadlock detection
* code will become invalid (reused for other purposes or made not present)
* while the detection is in progress.
*
* NS: RTLOCKVALREC*, RTTHREADINT and RTLOCKVALDRECSHRD::papOwners destruction.
* EW: Deadlock detection and some related activities.
*/
/** Serializing class tree insert and lookups. */
/** Class tree. */
/** Critical section serializing the teaching new rules to the classes. */
static RTCRITSECT g_LockValClassTeachCS;
/** Whether the lock validator is enabled or disabled.
* Only applies to new locks. */
static bool volatile g_fLockValidatorEnabled = true;
/** Set if the lock validator is quiet. */
#ifdef RT_STRICT
static bool volatile g_fLockValidatorQuiet = false;
#else
static bool volatile g_fLockValidatorQuiet = true;
#endif
/** Set if the lock validator may panic. */
#ifdef RT_STRICT
static bool volatile g_fLockValidatorMayPanic = true;
#else
static bool volatile g_fLockValidatorMayPanic = false;
#endif
/** Whether to return an error status on wrong locking order. */
static bool volatile g_fLockValSoftWrongOrder = false;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Lazy initialization of the lock validator globals.
*/
static void rtLockValidatorLazyInit(void)
{
static uint32_t volatile s_fInitializing = false;
if (ASMAtomicCmpXchgU32(&s_fInitializing, true, false))
{
/*
* The locks.
*/
RTLOCKVAL_SUB_CLASS_ANY, "RTLockVal-Teach");
if (g_hLockValClassTreeRWLock == NIL_RTSEMRW)
{
int rc = RTSemRWCreateEx(&hSemRW, RTSEMRW_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, "RTLockVal-Tree");
if (RT_SUCCESS(rc))
}
{
if (RT_SUCCESS(rc))
}
#ifdef IN_RING3
/*
* Check the environment for our config variables.
*/
if (RTEnvExist("IPRT_LOCK_VALIDATOR_ENABLED"))
if (RTEnvExist("IPRT_LOCK_VALIDATOR_DISABLED"))
ASMAtomicWriteBool(&g_fLockValidatorEnabled, false);
if (RTEnvExist("IPRT_LOCK_VALIDATOR_MAY_PANIC"))
if (RTEnvExist("IPRT_LOCK_VALIDATOR_MAY_NOT_PANIC"))
ASMAtomicWriteBool(&g_fLockValidatorMayPanic, false);
if (RTEnvExist("IPRT_LOCK_VALIDATOR_NOT_QUIET"))
ASMAtomicWriteBool(&g_fLockValidatorQuiet, false);
if (RTEnvExist("IPRT_LOCK_VALIDATOR_QUIET"))
ASMAtomicWriteBool(&g_fLockValidatorQuiet, true);
if (RTEnvExist("IPRT_LOCK_VALIDATOR_STRICT_ORDER"))
ASMAtomicWriteBool(&g_fLockValSoftWrongOrder, false);
if (RTEnvExist("IPRT_LOCK_VALIDATOR_SOFT_ORDER"))
#endif
/*
* Register cleanup
*/
/** @todo register some cleanup callback if we care. */
ASMAtomicWriteU32(&s_fInitializing, false);
}
}
/** Wrapper around ASMAtomicReadPtr. */
DECL_FORCE_INLINE(PRTLOCKVALRECUNION) rtLockValidatorReadRecUnionPtr(PRTLOCKVALRECUNION volatile *ppRec)
{
return p;
}
/** Wrapper around ASMAtomicWritePtr. */
DECL_FORCE_INLINE(void) rtLockValidatorWriteRecUnionPtr(PRTLOCKVALRECUNION volatile *ppRec, PRTLOCKVALRECUNION pRecNew)
{
}
/** Wrapper around ASMAtomicReadPtr. */
{
return p;
}
/** Wrapper around ASMAtomicUoReadPtr. */
DECL_FORCE_INLINE(PRTLOCKVALRECSHRDOWN) rtLockValidatorUoReadSharedOwner(PRTLOCKVALRECSHRDOWN volatile *ppOwner)
{
return p;
}
/**
* Reads a volatile thread handle field and returns the thread name.
*
* @returns Thread name (read only).
* @param phThread The thread handle field.
*/
{
if (!pThread)
return "<NIL>";
return "<INVALID>";
return "<BAD-THREAD-MAGIC>";
}
/**
* Launch a simple assertion like complaint w/ panic.
*
* @param pszFile Where from - file.
* @param iLine Where from - line.
* @param pszFunction Where from - function.
* @param pszWhat What we're complaining about.
* @param ... Format arguments.
*/
{
{
}
}
/**
* Describes the class.
*
* @param pszPrefix Message prefix.
* @param pClass The class to complain about.
* @param uSubClass My sub-class.
* @param fVerbose Verbose description including relations to other
* classes.
*/
static void rtLockValComplainAboutClass(const char *pszPrefix, RTLOCKVALCLASSINT *pClass, uint32_t uSubClass, bool fVerbose)
{
return;
/* Stringify the sub-class. */
const char *pszSubClass;
char szSubClass[32];
switch (uSubClass)
{
default:
break;
}
else
{
}
/* Validate the class pointer. */
{
return;
}
{
RTAssertMsg2AddWeak("%sbad class=%p magic=%#x sub-class=%s\n", pszPrefix, pClass, pClass->u32Magic, pszSubClass);
return;
}
/* OK, dump the class info. */
if (fVerbose)
{
uint32_t i = 0;
{
if (pCurClass != NIL_RTLOCKVALCLASS)
{
cPrinted == 0
? "Prior:"
: " ",
i,
? "autodidactic"
: "manually ",
cPrinted++;
}
}
if (!cPrinted)
RTAssertMsg2AddWeak("%sHash Stats: %u hits, %u misses\n", pszPrefix, pClass->cHashHits, pClass->cHashMisses);
#endif
}
else
{
{
if (pCurClass != NIL_RTLOCKVALCLASS)
{
if ((cPrinted % 10) == 0)
else
cPrinted++;
}
}
if (!cPrinted)
else if ((cPrinted % 10) != 0)
RTAssertMsg2AddWeak("\n");
}
}
/**
* Helper for getting the class name.
* @returns Class name string.
* @param pClass The class.
*/
{
if (!pClass)
return "<nil-class>";
return "<bad-class-ptr>";
return "<bad-class-magic>";
return "<no-class-name>";
}
/**
* Formats the sub-class.
*
* @returns Stringified sub-class.
* @param uSubClass The name.
* @param pszBuf Buffer that is big enough.
*/
{
switch (uSubClass)
{
case RTLOCKVAL_SUB_CLASS_NONE: return "none";
case RTLOCKVAL_SUB_CLASS_ANY: return "any";
default:
break;
}
else
return pszBuf;
}
/**
* Helper for rtLockValComplainAboutLock.
*/
DECL_FORCE_INLINE(void) rtLockValComplainAboutLockHlp(const char *pszPrefix, PRTLOCKVALRECUNION pRec, const char *pszSuffix,
const char *pszFrameType)
{
char szBuf[32];
switch (u32Magic)
{
case RTLOCKVALRECEXCL_MAGIC:
#ifdef RTLOCKVAL_WITH_VERBOSE_DUMPS
RTAssertMsg2AddWeak("%s%p %s xrec=%p own=%s r=%u cls=%s/%s pos={%Rbn(%u) %Rfn %p} [x%s]%s", pszPrefix,
#else
#endif
break;
case RTLOCKVALRECSHRD_MAGIC:
break;
{
#ifdef RTLOCKVAL_WITH_VERBOSE_DUMPS
RTAssertMsg2AddWeak("%s%p %s srec=%p trec=%p own=%s r=%u cls=%s/%s pos={%Rbn(%u) %Rfn %p} [o%s]%s", pszPrefix,
#else
#endif
else
break;
}
default:
}
}
/**
* Describes the lock.
*
* @param pszPrefix Message prefix.
* @param pRec The lock record we're working on.
* @param pszSuffix Message suffix.
*/
static void rtLockValComplainAboutLock(const char *pszPrefix, PRTLOCKVALRECUNION pRec, const char *pszSuffix)
{
# define FIX_REC(r) 1
#else
# define FIX_REC(r) (r)
#endif
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
case RTLOCKVALRECSHRD_MAGIC:
break;
break;
case RTLOCKVALRECNEST_MAGIC:
{
)
else
pRec->Nest.SrcPos.pszFile, pRec->Nest.SrcPos.uLine, pRec->Nest.SrcPos.pszFunction, pRec->Nest.SrcPos.uId,
break;
}
default:
RTAssertMsg2AddWeak("%spRec=%p u32Magic=%#x (bad)%s", pszPrefix, pRec, pRec->Core.u32Magic, pszSuffix);
break;
}
}
}
/**
* Dump the lock stack.
*
* @param pThread The thread which lock stack we're gonna dump.
* @param cchIndent The indentation in chars.
* @param cMinFrames The minimum number of frames to consider
* dumping.
* @param pHighightRec Record that should be marked specially in the
* dump.
*/
static void rtLockValComplainAboutLockStack(PRTTHREADINT pThread, unsigned cchIndent, uint32_t cMinFrames,
{
)
{
if (cEntries >= cMinFrames)
{
{
char szPrefix[80];
{
case RTLOCKVALRECSHRDOWN_MAGIC: pCur = rtLockValidatorReadRecUnionPtr(&pCur->ShrdOwner.pDown); break;
default:
break;
}
}
}
}
}
/**
* Launch the initial complaint.
*
* @param pszWhat What we're complaining about.
* @param pSrcPos Where we are complaining from, as it were.
* @param pThreadSelf The calling thread.
* @param pRec The main lock involved. Can be NULL.
* @param fDumpStack Whether to dump the lock stack (true) or not
* (false).
*/
static void rtLockValComplainFirst(const char *pszWhat, PCRTLOCKVALSRCPOS pSrcPos, PRTTHREADINT pThreadSelf,
{
{
ASMCompilerBarrier(); /* paranoia */
RTAssertMsg1Weak("RTLockValidator", pSrcPos ? pSrcPos->uLine : 0, pSrcPos ? pSrcPos->pszFile : NULL, pSrcPos ? pSrcPos->pszFunction : NULL);
RTAssertMsg2Weak("%s [uId=%p thrd=%s]\n", pszWhat, pSrcPos->uId, VALID_PTR(pThreadSelf) ? pThreadSelf->szName : "<NIL>");
else
RTAssertMsg2Weak("%s [thrd=%s]\n", pszWhat, VALID_PTR(pThreadSelf) ? pThreadSelf->szName : "<NIL>");
if (fDumpStack)
}
}
/**
* Continue bitching.
*
* @param pszFormat Format string.
* @param ... Format arguments.
*/
static void rtLockValComplainMore(const char *pszFormat, ...)
{
{
}
}
/**
* Raise a panic if enabled.
*/
static void rtLockValComplainPanic(void)
{
}
/**
* Copy a source position record.
*
* @param pDst The destination.
* @param pSrc The source. Can be NULL.
*/
{
if (pSrc)
{
}
else
{
}
}
/**
* Init a source position record.
*
* @param pSrcPos The source position record.
*/
{
#if HC_ARCH_BITS == 64
pSrcPos->u32Padding = 0;
#endif
}
/* sdbm:
This algorithm was created for sdbm (a public-domain reimplementation of
ndbm) database library. it was found to do well in scrambling bits,
causing better distribution of the keys and fewer splits. it also happens
to be a good general hashing function with good distribution. the actual
function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
is the faster version used in gawk. [there is even a faster, duff-device
version] the magic constant 65599 was picked out of thin air while
experimenting with different constants, and turns out to be a prime.
this is one of the algorithms used in berkeley db (see sleepycat) and
elsewhere. */
{
int c;
while ((c = *pu8++))
return hash;
}
/**
* Hashes the specified source position.
*
* @returns Hash.
* @param pSrcPos The source position record.
*/
{
|| pSrcPos->pszFunction)
{
uHash = 0;
if (pSrcPos->pszFunction)
}
else
{
}
return uHash;
}
/**
* Compares two source positions.
*
* @returns 0 if equal, < 0 if pSrcPos1 is smaller than pSrcPos2, > 0 if
* otherwise.
* @param pSrcPos1 The first source position.
* @param pSrcPos2 The second source position.
*/
{
if (iDiff != 0)
return iDiff;
if (iDiff != 0)
return iDiff;
return 0;
}
/**
* Serializes destruction of RTLOCKVALREC* and RTTHREADINT structures.
*/
DECLHIDDEN(void) rtLockValidatorSerializeDestructEnter(void)
{
if (hXRoads != NIL_RTSEMXROADS)
}
/**
* Call after rtLockValidatorSerializeDestructEnter.
*/
DECLHIDDEN(void) rtLockValidatorSerializeDestructLeave(void)
{
if (hXRoads != NIL_RTSEMXROADS)
}
/**
* Serializes deadlock detection against destruction of the objects being
* inspected.
*/
DECLINLINE(void) rtLockValidatorSerializeDetectionEnter(void)
{
if (hXRoads != NIL_RTSEMXROADS)
}
/**
* Call after rtLockValidatorSerializeDetectionEnter.
*/
DECLHIDDEN(void) rtLockValidatorSerializeDetectionLeave(void)
{
if (hXRoads != NIL_RTSEMXROADS)
}
/**
* Initializes the per thread lock validator data.
*
* @param pPerThread The data.
*/
{
/* ASSUMES the rest has already been zeroed. */
}
/**
* Delete the per thread lock validator data.
*
* @param pPerThread The data.
*/
{
/*
* Check that the thread doesn't own any locks at this time.
*/
if (pPerThread->pStackTop)
{
pPerThread->pStackTop, true);
}
/*
* Free the recursion records.
*/
while (pCur)
{
}
}
const char *pszNameFmt, ...)
{
int rc = RTLockValidatorClassCreateExV(phClass, pSrcPos, fAutodidact, fRecursionOk, fStrictReleaseOrder,
return rc;
}
{
/*
* Format the name and calc its length.
*/
char szName[32];
if (pszNameFmt && *pszNameFmt)
else
{
static uint32_t volatile s_cAnonymous = 0;
}
/*
* Figure out the file and function name lengths and allocate memory for
* it all.
*/
RTLOCKVALCLASSINT *pThis = (RTLOCKVALCLASSINT *)RTMemAllocVar(sizeof(*pThis) + cbFile + cbFunction + cbName);
if (!pThis)
return VERR_NO_MEMORY;
/*
* Initialize the class data.
*/
pThis->fDonateRefToNextRetainer = false;
pThis->afReserved[0] = false;
pThis->au32Reserved[i] = 0;
{
}
pThis->CreatePos.pszFile = pSrcPos->pszFile ? (char *)memcpy(pszDst, pSrcPos->pszFile, cbFile) : NULL;
pThis->CreatePos.pszFunction= pSrcPos->pszFunction ? (char *)memcpy(pszDst, pSrcPos->pszFunction, cbFunction) : NULL;
pThis->cHashMisses = 0;
#endif
return VINF_SUCCESS;
}
RTDECL(int) RTLockValidatorClassCreate(PRTLOCKVALCLASS phClass, bool fAutodidact, RT_SRC_POS_DECL, const char *pszNameFmt, ...)
{
1 /*cMsMinDeadlock*/, 1 /*cMsMinOrder*/,
pszNameFmt, va);
return rc;
}
/**
* Creates a new lock validator class with a reference that is consumed by the
* first call to RTLockValidatorClassRetain.
*
* This is tailored for use in the parameter list of a semaphore constructor.
*
* @returns Class handle with a reference that is automatically consumed by the
* first retainer. NIL_RTLOCKVALCLASS if we run into trouble.
*
* @param pszFile The source position of the call, file.
* @param iLine The source position of the call, line.
* @param pszFunction The source position of the call, function.
* @param pszNameFmt Class name format string, optional (NULL). Max
* length is 32 bytes.
* @param ... Format string arguments.
*/
RTDECL(RTLOCKVALCLASS) RTLockValidatorClassCreateUnique(RT_SRC_POS_DECL, const char *pszNameFmt, ...)
{
true /*fAutodidact*/, true /*fRecursionOk*/, false /*fStrictReleaseOrder*/,
1 /*cMsMinDeadlock*/, 1 /*cMsMinOrder*/,
pszNameFmt, va);
if (RT_FAILURE(rc))
return NIL_RTLOCKVALCLASS;
return pClass;
}
/**
* Internal class retainer.
* @returns The new reference count.
* @param pClass The class.
*/
{
if (cRefs > RTLOCKVALCLASS_MAX_REFS)
else if ( cRefs == 2
return cRefs;
}
/**
* Validates and retains a lock validator class.
*
* @returns @a hClass on success, NIL_RTLOCKVALCLASS on failure.
* @param hClass The class handle. NIL_RTLOCKVALCLASS is ok.
*/
{
if (hClass == NIL_RTLOCKVALCLASS)
return hClass;
return hClass;
}
/**
* Internal class releaser.
* @returns The new reference count.
* @param pClass The class.
*/
{
else if (!cRefs)
return cRefs;
}
/**
* Destroys a class once there are not more references to it.
*
* @param Class The class.
*/
{
while (pChunk)
{
{
if (pClass2 != NIL_RTLOCKVALCLASS)
{
}
}
}
}
{
if (g_hLockValClassTreeRWLock == NIL_RTSEMRW)
while (pClass)
{
break;
}
if (RT_SUCCESS(rcLock))
return pClass;
}
{
if (hClass == NIL_RTLOCKVALCLASS)
{
/*
* Create a new class and insert it into the tree.
*/
true /*fAutodidact*/, true /*fRecursionOk*/, false /*fStrictReleaseOrder*/,
1 /*cMsMinDeadlock*/, 1 /*cMsMinOrder*/,
pszNameFmt, va);
if (RT_SUCCESS(rc))
{
if (g_hLockValClassTreeRWLock == NIL_RTSEMRW)
if (RT_SUCCESS(rcLock))
return hClass;
}
}
return hClass;
}
{
return rtLockValidatorClassRetain(pClass);
}
{
if (pClass == NIL_RTLOCKVALCLASS)
return 0;
return rtLockValidatorClassRelease(pClass);
}
/**
* Worker for rtLockValidatorClassIsPriorClass that does a linear search thru
* all the chunks for @a pPriorClass.
*
* @returns true / false.
* @param pClass The class to search.
* @param pPriorClass The class to search for.
*/
static bool rtLockValidatorClassIsPriorClassByLinearSearch(RTLOCKVALCLASSINT *pClass, RTLOCKVALCLASSINT *pPriorClass)
{
{
{
{
}
/* update the hash table entry. */
if ( !(*ppHashEntry)
#endif
return true;
}
}
return false;
}
/**
* Checks if @a pPriorClass is a known prior class.
*
* @returns true / false.
* @param pClass The class to search.
* @param pPriorClass The class to search for.
*/
DECL_FORCE_INLINE(bool) rtLockValidatorClassIsPriorClass(RTLOCKVALCLASSINT *pClass, RTLOCKVALCLASSINT *pPriorClass)
{
/*
* Hash lookup here.
*/
if ( pRef
{
#endif
return true;
}
}
/**
* Adds a class to the prior list.
*
* @returns VINF_SUCCESS, VERR_NO_MEMORY or VERR_SEM_LV_WRONG_ORDER.
* @param pClass The class to work on.
* @param pPriorClass The class to add.
* @param fAutodidacticism Whether we're teaching ourselfs (true) or
* somebody is teaching us via the API (false).
* @param pSrcPos Where this rule was added (optional).
*/
static int rtLockValidatorClassAddPriorClass(RTLOCKVALCLASSINT *pClass, RTLOCKVALCLASSINT *pPriorClass,
{
/*
* Check that there are no conflict (no assert since we might race each other).
*/
int rc = VERR_SEM_LV_INTERNAL_ERROR;
{
{
/*
* Scan the table for a free entry, allocating a new chunk if necessary.
*/
{
bool fDone = false;
{
if (fDone)
{
rc = VINF_SUCCESS;
break;
}
}
if (fDone)
break;
/* If no more chunks, allocate a new one and insert the class before linking it. */
{
if (!pNew)
{
rc = VERR_NO_MEMORY;
break;
}
{
}
rc = VINF_SUCCESS;
break;
}
} /* chunk loop */
}
else
rc = VINF_SUCCESS;
}
else
if (RT_SUCCESS(rcLock))
return rc;
}
{
}
{
return VINF_SUCCESS;
}
/**
* Unlinks all siblings.
*
* This is used during record deletion and assumes no races.
*
* @param pCore One of the siblings.
*/
{
/* ASSUMES sibling destruction doesn't involve any races and that all
related records are to be disposed off now. */
while (pSibling)
{
PRTLOCKVALRECUNION volatile *ppCoreNext;
{
case RTLOCKVALRECEXCL_MAGIC:
break;
case RTLOCKVALRECSHRD_MAGIC:
break;
default:
AssertFailed();
ppCoreNext = NULL;
break;
}
if (RT_UNLIKELY(ppCoreNext))
break;
}
}
{
/*
* Validate input.
*/
/*
* Link them (circular list).
*/
{
}
{
}
else
return VINF_SUCCESS;
}
/**
* Gets the lock name for the given record.
*
* @returns Read-only lock name.
* @param pRec The lock record.
*/
{
{
case RTLOCKVALRECEXCL_MAGIC:
case RTLOCKVALRECSHRD_MAGIC:
case RTLOCKVALRECNEST_MAGIC:
{
{
case RTLOCKVALRECEXCL_MAGIC:
case RTLOCKVALRECSHRD_MAGIC:
default:
return "unknown-nested";
}
}
return "orphaned-nested";
default:
return "unknown";
}
}
/**
* Gets the class for this locking record.
*
* @returns Pointer to the class or NIL_RTLOCKVALCLASS.
* @param pRec The lock validator record.
*/
{
{
case RTLOCKVALRECEXCL_MAGIC:
case RTLOCKVALRECSHRD_MAGIC:
{
return pSharedRec->hClass;
return NIL_RTLOCKVALCLASS;
}
case RTLOCKVALRECNEST_MAGIC:
{
{
{
case RTLOCKVALRECEXCL_MAGIC:
{
return pSharedRec->hClass;
break;
}
default:
break;
}
}
return NIL_RTLOCKVALCLASS;
}
default:
return NIL_RTLOCKVALCLASS;
}
}
/**
* Gets the class for this locking record and the pointer to the one below it in
* the stack.
*
* @returns Pointer to the class or NIL_RTLOCKVALCLASS.
* @param pRec The lock validator record.
* @param puSubClass Where to return the sub-class.
* @param ppDown Where to return the pointer to the record below.
*/
rtLockValidatorRecGetClassesAndDown(PRTLOCKVALRECUNION pRec, uint32_t *puSubClass, PRTLOCKVALRECUNION *ppDown)
{
{
case RTLOCKVALRECEXCL_MAGIC:
case RTLOCKVALRECSHRD_MAGIC:
{
{
return pSharedRec->hClass;
}
return NIL_RTLOCKVALCLASS;
}
case RTLOCKVALRECNEST_MAGIC:
{
{
{
case RTLOCKVALRECEXCL_MAGIC:
{
{
return pSharedRec->hClass;
}
break;
}
default:
break;
}
}
return NIL_RTLOCKVALCLASS;
}
default:
return NIL_RTLOCKVALCLASS;
}
}
/**
* Gets the sub-class for a lock record.
*
* @returns the sub-class.
* @param pRec The lock validator record.
*/
{
{
case RTLOCKVALRECEXCL_MAGIC:
case RTLOCKVALRECSHRD_MAGIC:
{
return pSharedRec->uSubClass;
return RTLOCKVAL_SUB_CLASS_NONE;
}
case RTLOCKVALRECNEST_MAGIC:
{
{
{
case RTLOCKVALRECEXCL_MAGIC:
{
return pSharedRec->uSubClass;
break;
}
default:
break;
}
}
return RTLOCKVAL_SUB_CLASS_NONE;
}
default:
return RTLOCKVAL_SUB_CLASS_NONE;
}
}
/**
* Calculates the depth of a lock stack.
*
* @returns Number of stack frames.
* @param pThread The thread.
*/
{
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
case RTLOCKVALRECNEST_MAGIC:
break;
default:
}
cEntries++;
}
return cEntries;
}
/**
* Checks if the stack contains @a pRec.
*
* @returns true / false.
* @param pThreadSelf The curren thread.
* @param pRec The lock record.
*/
{
while (pCur)
{
AssertPtrReturn(pCur, false);
return true;
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
case RTLOCKVALRECNEST_MAGIC:
break;
default:
}
}
return false;
}
/**
* Pushes a lock record onto the stack.
*
* @param pThreadSelf The current thread.
* @param pRec The lock record.
*/
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
default:
}
}
/**
* Pops a lock record off the stack.
*
* @param pThreadSelf The current thread.
* @param pRec The lock.
*/
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
default:
}
else
{
/* Find the pointer to our record and unlink ourselves. */
while (pCur)
{
PRTLOCKVALRECUNION volatile *ppDown;
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
case RTLOCKVALRECNEST_MAGIC:
break;
default:
}
{
return;
}
}
}
}
/**
* Creates and pushes lock recursion record onto the stack.
*
* @param pThreadSelf The current thread.
* @param pRec The lock record.
* @param pSrcPos Where the recursion occured.
*/
static void rtLockValidatorStackPushRecursion(PRTTHREADINT pThreadSelf, PRTLOCKVALRECUNION pRec, PCRTLOCKVALSRCPOS pSrcPos)
{
/*
* Allocate a new recursion record
*/
if (pRecursionRec)
else
{
if (!pRecursionRec)
return;
}
/*
* Initialize it.
*/
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
default:
return;
}
/*
* Link it.
*/
rtLockValidatorWriteRecUnionPtr(&pThreadSelf->LockValidator.pStackTop, (PRTLOCKVALRECUNION)pRecursionRec);
#endif /* RTLOCKVAL_WITH_RECURSION_RECORDS */
}
/**
* Pops a lock recursion record off the stack.
*
* @param pThreadSelf The current thread.
* @param pRec The lock record.
*/
{
{
}
/*
* Pop the recursion record.
*/
)
{
}
else
{
/* Find the record above ours. */
for (;;)
{
{
case RTLOCKVALRECEXCL_MAGIC:
continue;
continue;
case RTLOCKVALRECNEST_MAGIC:
break;
continue;
default:
}
break; /* ugly */
}
}
/*
* Invalidate and free the record.
*/
#endif /* RTLOCKVAL_WITH_RECURSION_RECORDS */
}
/**
* Helper for rtLockValidatorStackCheckLockingOrder that does the bitching and
* returns VERR_SEM_LV_WRONG_ORDER.
*/
static int rtLockValidatorStackWrongOrder(const char *pszWhat, PCRTLOCKVALSRCPOS pSrcPos, PRTTHREADINT pThreadSelf,
{
rtLockValComplainAboutClass("My class: ", pClass1, rtLockValidatorRecGetSubClass(pRec1), true /*fVerbose*/);
rtLockValComplainAboutClass("Other class: ", pClass2, rtLockValidatorRecGetSubClass(pRec2), true /*fVerbose*/);
}
/**
* Checks if the sub-class order is ok or not.
*
* Used to deal with two locks from the same class.
*
* @returns true if ok, false if not.
* @param uSubClass1 The sub-class of the lock that is being
* considered.
* @param uSubClass2 The sub-class of the lock that is already being
* held.
*/
{
if (uSubClass1 > uSubClass2)
{
/* NONE kills ANY. */
if (uSubClass2 == RTLOCKVAL_SUB_CLASS_NONE)
return false;
return true;
}
/* ANY counters all USER values. (uSubClass1 == NONE only if they are equal) */
if (uSubClass1 == RTLOCKVAL_SUB_CLASS_ANY)
return true;
return false;
}
/**
* Checks if the class and sub-class lock order is ok.
*
* @returns true if ok, false if not.
* @param pClass1 The class of the lock that is being considered.
* @param uSubClass1 The sub-class that goes with @a pClass1.
* @param pClass2 The class of the lock that is already being
* held.
* @param uSubClass2 The sub-class that goes with @a pClass2.
*/
DECL_FORCE_INLINE(bool) rtLockValidatorIsClassOrderOk(RTLOCKVALCLASSINT *pClass1, uint32_t uSubClass1,
{
}
/**
* Checks the locking order, part two.
*
* @returns VINF_SUCCESS, VERR_SEM_LV_WRONG_ORDER or VERR_SEM_LV_INTERNAL_ERROR.
* @param pClass The lock class.
* @param uSubClass The lock sub-class.
* @param pThreadSelf The current thread.
* @param pRec The lock record.
* @param pSrcPos The source position of the locking operation.
*/
static int rtLockValidatorStackCheckLockingOrder2(RTLOCKVALCLASSINT * const pClass, uint32_t const uSubClass,
PCRTLOCKVALSRCPOS const pSrcPos,
RTLOCKVALCLASSINT * const pFirstBadClass,
PRTLOCKVALRECUNION const pFirstBadRec,
PRTLOCKVALRECUNION const pFirstBadDown)
{
/*
* Something went wrong, pCur is pointing to where.
*/
if ( pClass == pFirstBadClass
if (!pClass->fAutodidact)
/*
* This class is an autodidact, so we have to check out the rest of the stack
* for direct violations.
*/
while (pCur)
{
else
{
RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
if (pPriorClass != NIL_RTLOCKVALCLASS)
{
{
if ( pClass == pPriorClass
cNewRules++;
}
}
}
}
if (cNewRules == 1)
{
/*
* Special case the simple operation, hoping that it will be a
* frequent case.
*/
int rc = rtLockValidatorClassAddPriorClass(pClass, pFirstBadClass, true /*fAutodidacticism*/, pSrcPos);
if (rc == VERR_SEM_LV_WRONG_ORDER)
}
else
{
/*
* We may be adding more than one rule, so we have to take the lock
* before starting to add the rules. This means we have to check
* the state after taking it since we might be racing someone adding
* a conflicting rule.
*/
/* Check */
pCur = pFirstBadRec;
while (pCur)
{
else
{
RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
if (pPriorClass != NIL_RTLOCKVALCLASS)
{
{
if ( pClass == pPriorClass
{
if (RT_SUCCESS(rcLock))
}
}
}
}
}
/* Iterate the stack yet again, adding new rules this time. */
pCur = pFirstBadRec;
while (pCur)
{
else
{
RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
if (pPriorClass != NIL_RTLOCKVALCLASS)
{
{
int rc = rtLockValidatorClassAddPriorClass(pClass, pPriorClass, true /*fAutodidacticism*/, pSrcPos);
if (RT_FAILURE(rc))
{
break;
}
}
}
}
}
if (RT_SUCCESS(rcLock))
}
return VINF_SUCCESS;
}
/**
* Checks the locking order.
*
* @returns VINF_SUCCESS, VERR_SEM_LV_WRONG_ORDER or VERR_SEM_LV_INTERNAL_ERROR.
* @param pClass The lock class.
* @param uSubClass The lock sub-class.
* @param pThreadSelf The current thread.
* @param pRec The lock record.
* @param pSrcPos The source position of the locking operation.
*/
static int rtLockValidatorStackCheckLockingOrder(RTLOCKVALCLASSINT * const pClass, uint32_t const uSubClass,
{
/*
* Some internal paranoia first.
*/
/*
* Walk the stack, delegate problems to a worker routine.
*/
if (!pCur)
return VINF_SUCCESS;
for (;;)
{
else
{
RTLOCKVALCLASSINT *pPriorClass = rtLockValidatorRecGetClassesAndDown(pCur, &uPriorSubClass, &pDown);
if (pPriorClass != NIL_RTLOCKVALCLASS)
{
}
}
if (!pCur)
return VINF_SUCCESS;
}
}
/**
* Check that the lock record is the topmost one on the stack, complain and fail
* if it isn't.
*
* @returns VINF_SUCCESS, VERR_SEM_LV_WRONG_RELEASE_ORDER or
* VERR_SEM_LV_INVALID_PARAMETER.
* @param pThreadSelf The current thread.
* @param pRec The record.
*/
{
|| ( pTop
return VINF_SUCCESS;
/* Look for a recursion record so the right frame is dumped and marked. */
while (pTop)
{
{
{
break;
}
}
else
break;
}
#endif
}
/**
* Checks if all owners are blocked - shared record operated in signaller mode.
*
* @returns true / false accordingly.
* @param pRec The record.
* @param pThreadSelf The current thread.
*/
DECL_FORCE_INLINE(bool) rtLockValidatorDdAreAllThreadsBlocked(PRTLOCKVALRECSHRD pRec, PRTTHREADINT pThreadSelf)
{
if (cEntries == 0)
return false;
for (uint32_t i = 0; i < cAllocated; i++)
{
if ( pEntry
{
if (!pCurThread)
return false;
return false;
&& pCurThread != pThreadSelf)
return false;
if (--cEntries == 0)
break;
}
else
}
return true;
}
/**
* Verifies the deadlock stack before calling it a deadlock.
*
* @retval VERR_SEM_LV_DEADLOCK if it's a deadlock.
* @retval VERR_SEM_LV_ILLEGAL_UPGRADE if it's a deadlock on the same lock.
* @retval VERR_TRY_AGAIN if something changed.
*
* @param pStack The deadlock detection stack.
* @param pThreadSelf The current thread.
*/
{
{
for (uint32_t i = 1; i < c; i++)
{
return VERR_TRY_AGAIN;
return VERR_TRY_AGAIN;
return VERR_TRY_AGAIN;
/* ASSUMES the signaller records won't have siblings! */
return VERR_TRY_AGAIN;
}
}
if (c == 1)
return VERR_SEM_LV_ILLEGAL_UPGRADE;
return VERR_SEM_LV_DEADLOCK;
}
/**
* Checks for stack cycles caused by another deadlock before returning.
*
* @retval VINF_SUCCESS if the stack is simply too small.
* @retval VERR_SEM_LV_EXISTING_DEADLOCK if a cycle was detected.
*
* @param pStack The deadlock detection stack.
*/
{
{
return VERR_SEM_LV_EXISTING_DEADLOCK;
}
static bool volatile s_fComplained = false;
if (!s_fComplained)
{
s_fComplained = true;
rtLockValComplain(RT_SRC_POS, "lock validator stack is too small! (%zu entries)\n", RT_ELEMENTS(pStack->a));
}
return VINF_SUCCESS;
}
/**
* Worker for rtLockValidatorDeadlockDetection that does the actual deadlock
* detection.
*
* @retval VINF_SUCCESS
* @retval VERR_SEM_LV_DEADLOCK
* @retval VERR_SEM_LV_EXISTING_DEADLOCK
* @retval VERR_SEM_LV_ILLEGAL_UPGRADE
* @retval VERR_TRY_AGAIN
*
* @param pStack The stack to use.
* @param pOriginalRec The original record.
* @param pThreadSelf The calling thread.
*/
static int rtLockValidatorDdDoDetection(PRTLOCKVALDDSTACK pStack, PRTLOCKVALRECUNION const pOriginalRec,
PRTTHREADINT const pThreadSelf)
{
pStack->c = 0;
/* We could use a single RTLOCKVALDDENTRY variable here, but the
compiler may make a better job of it when using individual variables. */
{
/*
* Process the current record.
*/
/* Find the next relevant owner thread and record. */
{
case RTLOCKVALRECEXCL_MAGIC:
for (;;)
{
if ( !pNextThread
break;
if ( !RTTHREAD_IS_SLEEPING(enmNextState)
&& pNextThread != pThreadSelf)
break;
break;
}
if (!pNextRec)
{
if ( pRec
&& pRec != pFirstSibling)
continue;
}
break;
case RTLOCKVALRECSHRD_MAGIC:
{
/* Skip to the next sibling if same side. ASSUMES reader priority. */
/** @todo The read side of a read-write lock is problematic if
* the implementation prioritizes writers over readers because
* that means we should could deadlock against current readers
* if a writer showed up. If the RW sem implementation is
* wrapping some native API, it's not so easy to detect when we
* should do this and when we shouldn't. Checking when we
* shouldn't is subject to wakeup scheduling and cannot easily
* be made reliable.
*
* At the moment we circumvent all this mess by declaring that
* readers has priority. This is TRUE on linux, but probably
* isn't on Solaris and FreeBSD. */
if ( pRec == pFirstSibling
{
continue;
}
}
/* Scan the owner table for blocked owners. */
|| iEntry != UINT32_MAX
)
)
{
while (++iEntry < cAllocated)
{
if (pEntry)
{
for (;;)
{
break;
if ( !pNextThread
break;
if ( !RTTHREAD_IS_SLEEPING(enmNextState)
&& pNextThread != pThreadSelf)
break;
break;
}
if (pNextRec)
break;
}
else
}
if (pNextRec)
break;
}
/* Advance to the next sibling, if any. */
&& pRec != pFirstSibling)
{
iEntry = UINT32_MAX;
continue;
}
break;
break;
default:
break;
}
if (pNextRec)
{
/*
* Recurse and check for deadlock.
*/
pStack->c++;
&& ( i != 0
)
)
iEntry = UINT32_MAX;
}
else
{
/*
* No deadlock here, unwind the stack and deal with any unfinished
* business there.
*/
for (;;)
{
/* pop */
if (i == 0)
return VINF_SUCCESS;
i--;
/* Examine it. */
if (u32Magic == RTLOCKVALRECEXCL_MAGIC)
else if (u32Magic == RTLOCKVALRECSHRD_MAGIC)
{
break; /* continue processing this record. */
}
else
{
|| u32Magic == RTLOCKVALRECSHRD_MAGIC_DEAD);
continue;
}
/* Any next record to advance to? */
if ( !pRec
continue;
iEntry = UINT32_MAX;
break;
}
/* Restore the rest of the state and update the stack. */
pStack->c = i;
}
}
}
/**
* Check for the simple no-deadlock case.
*
* @returns true if no deadlock, false if further investigation is required.
*
* @param pOriginalRec The original record.
*/
{
{
if ( !pThread
return true;
if (!RTTHREAD_IS_SLEEPING(enmState))
return true;
}
return false;
}
/**
* Worker for rtLockValidatorDeadlockDetection that bitches about a deadlock.
*
* @param pStack The chain of locks causing the deadlock.
* @param pRec The record relating to the current thread's lock
* operation.
* @param pThreadSelf This thread.
* @param pSrcPos Where we are going to deadlock.
* @param rc The return code.
*/
{
{
const char *pszWhat;
switch (rc)
{
}
rtLockValComplainFirst(pszWhat, pSrcPos, pThreadSelf, pStack->a[0].pRec != pRec ? pRec : NULL, true);
{
char szPrefix[24];
{
}
else
{
}
}
rtLockValComplainMore("---- end of deadlock chain ----\n");
}
}
/**
* Perform deadlock detection.
*
* @retval VINF_SUCCESS
* @retval VERR_SEM_LV_DEADLOCK
* @retval VERR_SEM_LV_EXISTING_DEADLOCK
* @retval VERR_SEM_LV_ILLEGAL_UPGRADE
*
* @param pRec The record relating to the current thread's lock
* operation.
* @param pThreadSelf The current thread.
* @param pSrcPos The position of the current lock operation.
*/
static int rtLockValidatorDeadlockDetection(PRTLOCKVALRECUNION pRec, PRTTHREADINT pThreadSelf, PCRTLOCKVALSRCPOS pSrcPos)
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
if (rc == VERR_TRY_AGAIN)
{
{
if (RT_SUCCESS_NP(rc))
return VINF_SUCCESS;
if (rc != VERR_TRY_AGAIN)
break;
if (iLoop >= 3)
return VINF_SUCCESS;
}
}
return rc;
}
RTDECL(void) RTLockValidatorRecExclInitV(PRTLOCKVALRECEXCL pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
{
|| uSubClass == RTLOCKVAL_SUB_CLASS_ANY);
pRec->afReserved[0] = 0;
pRec->cRecursion = 0;
if (pszNameFmt)
else
{
static uint32_t volatile s_cAnonymous = 0;
}
/* Lazy initialization. */
}
RTDECL(void) RTLockValidatorRecExclInit(PRTLOCKVALRECEXCL pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
{
}
{
if (!pRec)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
const char *pszNameFmt, ...)
{
return rc;
}
{
if (hClass != NIL_RTLOCKVALCLASS)
}
{
if (pRec)
{
}
}
{
|| uSubClass == RTLOCKVAL_SUB_CLASS_ANY,
}
{
return;
if (hThreadSelf == NIL_RTTHREAD)
{
}
{
}
else
{
}
}
/**
* Internal worker for RTLockValidatorRecExclReleaseOwner and
* RTLockValidatorRecExclReleaseOwnerUnchecked.
*/
static void rtLockValidatorRecExclReleaseOwnerUnchecked(PRTLOCKVALRECUNION pRec, bool fFinalRecursion)
{
if (c == 0)
{
}
else
{
}
}
{
return VINF_SUCCESS;
/*
* Check the release order.
*/
)
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Join paths with RTLockValidatorRecExclReleaseOwnerUnchecked.
*/
return VINF_SUCCESS;
}
{
}
{
return VINF_SUCCESS;
{
rtLockValComplainFirst("Recursion not allowed by the class!",
return VERR_SEM_LV_NESTED;
}
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
/*
* Check the release order.
*/
)
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Perform the unwind.
*/
return VINF_SUCCESS;
}
RTDECL(int) RTLockValidatorRecExclRecursionMixed(PRTLOCKVALRECEXCL pRec, PRTLOCKVALRECCORE pRecMixed, PCRTLOCKVALSRCPOS pSrcPos)
{
return VINF_SUCCESS;
{
rtLockValComplainFirst("Mixed recursion not allowed by the class!",
return VERR_SEM_LV_NESTED;
}
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
/*
* Check the release order.
*/
)
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Perform the unwind.
*/
return VINF_SUCCESS;
}
{
/*
* Validate and adjust input. Quit early if order validation is disabled.
*/
return VINF_SUCCESS;
if (hThreadSelf == NIL_RTTHREAD)
{
}
/*
* Detect recursion as it isn't subject to order restrictions.
*/
return VINF_SUCCESS;
return rtLockValidatorStackCheckLockingOrder(pRecU->Excl.hClass, pRecU->Excl.uSubClass, hThreadSelf, pRecU, pSrcPos);
}
{
/*
* Fend off wild life.
*/
return VINF_SUCCESS;
{
}
/*
* Record the location.
*/
/*
* Don't do deadlock detection if we're recursing.
*
* On some hosts we don't do recursion accounting our selves and there
* isn't any other place to check for this.
*/
int rc = VINF_SUCCESS;
{
if ( !fRecursiveOk
{
}
}
/*
* Perform deadlock detection.
*/
rc = VINF_SUCCESS;
else if (!rtLockValidatorIsSimpleNoDeadlockCase(pRecU))
if (RT_SUCCESS(rc))
else
{
}
return rc;
}
RTDECL(int) RTLockValidatorRecExclCheckOrderAndBlocking(PRTLOCKVALRECEXCL pRec, RTTHREAD hThreadSelf,
{
if (RT_SUCCESS(rc))
return rc;
}
RTDECL(void) RTLockValidatorRecSharedInitV(PRTLOCKVALRECSHRD pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
{
|| uSubClass == RTLOCKVAL_SUB_CLASS_ANY);
/* the table */
pRec->iLastEntry = 0;
pRec->cAllocated = 0;
pRec->fReallocating = false;
/* the name */
if (pszNameFmt)
else
{
static uint32_t volatile s_cAnonymous = 0;
}
}
RTDECL(void) RTLockValidatorRecSharedInit(PRTLOCKVALRECSHRD pRec, RTLOCKVALCLASS hClass, uint32_t uSubClass,
{
RTLockValidatorRecSharedInitV(pRec, hClass, uSubClass, hLock, fSignaller, fEnabled, pszNameFmt, va);
}
{
/*
* Flip it into table realloc mode and take the destruction lock.
*/
{
}
{
}
if (hClass != NIL_RTLOCKVALCLASS)
}
{
|| uSubClass == RTLOCKVAL_SUB_CLASS_ANY,
}
/**
* Locates an owner (thread) in a shared lock record.
*
* @returns Pointer to the owner entry on success, NULL on failure..
* @param pShared The shared lock record.
* @param hThread The thread (owner) to find.
* @param piEntry Where to optionally return the table in index.
* Optional.
*/
{
if (papOwners)
{
{
PRTLOCKVALRECUNION pEntry = (PRTLOCKVALRECUNION)rtLockValidatorUoReadSharedOwner(&papOwners[iEntry]);
{
if (piEntry)
return pEntry;
}
}
}
return NULL;
}
{
/*
* Validate and adjust input. Quit early if order validation is disabled.
*/
)
return VINF_SUCCESS;
if (hThreadSelf == NIL_RTTHREAD)
{
}
/*
* Detect recursion as it isn't subject to order restrictions.
*/
if (pEntry)
return VINF_SUCCESS;
return rtLockValidatorStackCheckLockingOrder(pRecU->Shared.hClass, pRecU->Shared.uSubClass, hThreadSelf, pRecU, pSrcPos);
}
{
/*
* Fend off wild life.
*/
return VINF_SUCCESS;
{
}
/*
* Record the location.
*/
/*
* Don't do deadlock detection if we're recursing.
*/
int rc = VINF_SUCCESS;
: NULL;
if (pEntry)
{
if ( !fRecursiveOk
)
{
}
}
/*
* Perform deadlock detection.
*/
rc = VINF_SUCCESS;
else if (!rtLockValidatorIsSimpleNoDeadlockCase(pRecU))
if (RT_SUCCESS(rc))
else
{
}
return rc;
}
RTDECL(int) RTLockValidatorRecSharedCheckOrderAndBlocking(PRTLOCKVALRECSHRD pRec, RTTHREAD hThreadSelf,
{
if (RT_SUCCESS(rc))
return rc;
}
/**
* Allocates and initializes an owner entry for the shared lock record.
*
* @returns The new owner entry.
* @param pRec The shared lock record.
* @param pThreadSelf The calling thread and owner. Used for record
* initialization and allocation.
* @param pSrcPos The source position.
*/
rtLockValidatorRecSharedAllocOwner(PRTLOCKVALRECSHRD pRec, PRTTHREADINT pThreadSelf, PCRTLOCKVALSRCPOS pSrcPos)
{
/*
* Check if the thread has any statically allocated records we can easily
* make use of.
*/
unsigned iEntry = ASMBitFirstSetU32(ASMAtomicUoReadU32(&pThreadSelf->LockValidator.bmFreeShrdOwners));
if ( iEntry > 0
{
}
else
{
if (RT_UNLIKELY(!pEntry))
return NULL;
}
#if HC_ARCH_BITS == 32
#endif
if (pSrcPos)
else
return pEntry;
}
/**
* Frees an owner entry allocated by rtLockValidatorRecSharedAllocOwner.
*
* @param pEntry The owner entry.
*/
{
if (pEntry)
{
if (pEntry->fStaticAlloc)
{
}
else
{
}
}
}
/**
* Make more room in the table.
*
* @retval true on success
* @retval false if we're out of memory or running into a bad race condition
* (probably a bug somewhere). No longer holding the lock.
*
* @param pShared The shared lock record.
*/
{
for (unsigned i = 0; i < 1000; i++)
{
/*
* Switch to the other data access direction.
*/
if (i >= 10)
{
RTThreadSleep(i >= 100);
}
/*
* Try grab the privilege to reallocating the table.
*/
{
{
/*
* Ok, still not enough space. Reallocate the table.
*/
#if 0 /** @todo enable this after making sure growing works flawlessly. */
#else
#endif
(cAllocated + cInc) * sizeof(void *));
if (!papOwners)
{
/* RTMemRealloc will assert */
return false;
}
while (cInc-- > 0)
{
cAllocated++;
}
}
}
break;
return true;
}
AssertFailed(); /* too many iterations or destroyed while racing. */
return false;
}
/**
* Adds an owner entry to a shared lock record.
*
* @returns true on success, false on serious race or we're if out of memory.
* @param pShared The shared lock record.
* @param pEntry The owner entry.
*/
DECLINLINE(bool) rtLockValidatorRecSharedAddOwner(PRTLOCKVALRECSHRD pShared, PRTLOCKVALRECSHRDOWN pEntry)
{
{
return false; /* the worker leave the lock */
for (unsigned i = 0; i < 100; i++)
{
{
{
return true;
}
}
Assert(i != 25);
}
AssertFailed();
}
return false;
}
/**
* Remove an owner entry from a shared lock record and free it.
*
* @param pShared The shared lock record.
* @param pEntry The owner entry to remove.
* @param iEntry The last known index.
*/
DECLINLINE(void) rtLockValidatorRecSharedRemoveAndFreeOwner(PRTLOCKVALRECSHRD pShared, PRTLOCKVALRECSHRDOWN pEntry,
{
/*
* Remove it from the table.
*/
AssertReturnVoidStmt(pShared->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, rtLockValidatorSerializeDetectionLeave());
{
/* this shouldn't happen yet... */
AssertFailed();
break;
}
/*
* Successfully removed, now free it.
*/
}
RTDECL(void) RTLockValidatorRecSharedResetOwner(PRTLOCKVALRECSHRD pRec, RTTHREAD hThread, PCRTLOCKVALSRCPOS pSrcPos)
{
return;
/*
* Free all current owners.
*/
{
AssertReturnVoidStmt(pRec->Core.u32Magic == RTLOCKVALRECSHRD_MAGIC, rtLockValidatorSerializeDetectionLeave());
{
PRTLOCKVALRECSHRDOWN pEntry = (PRTLOCKVALRECSHRDOWN)ASMAtomicXchgPtr((void * volatile *)&papEntries[iEntry], NULL);
if (pEntry)
{
break;
}
iEntry++;
}
}
if (hThread != NIL_RTTHREAD)
{
/*
* Allocate a new owner entry and insert it into the table.
*/
if ( pEntry
}
}
RTDECL(void) RTLockValidatorRecSharedAddOwner(PRTLOCKVALRECSHRD pRec, RTTHREAD hThread, PCRTLOCKVALSRCPOS pSrcPos)
{
return;
if (hThread == NIL_RTTHREAD)
{
}
/*
* Recursive?
*
* Note! This code can be optimized to try avoid scanning the table on
* insert. However, that's annoying work that makes the code big,
* so it can wait til later sometime.
*/
if (pEntry)
{
return;
}
/*
* Allocate a new owner entry and insert it into the table.
*/
if (pEntry)
{
{
if (!pRec->fSignaller)
}
else
}
}
{
return;
if (hThread == NIL_RTTHREAD)
{
}
/*
* Find the entry hope it's a recursive one.
*/
if (c == 0)
{
if (!pRec->fSignaller)
}
else
{
}
}
{
/* Validate and resolve input. */
return false;
if (hThread == NIL_RTTHREAD)
{
}
/* Do the job. */
}
{
return VINF_SUCCESS;
if (hThreadSelf == NIL_RTTHREAD)
{
}
/*
* Locate the entry for this thread in the table.
*/
if (RT_UNLIKELY(!pEntry))
{
return VERR_SEM_LV_NOT_OWNER;
}
/*
* Check the release order.
*/
)
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Release the ownership or unwind a level of recursion.
*/
if (c == 0)
{
}
else
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
if (hThreadSelf == NIL_RTTHREAD)
{
}
/*
* Locate the entry for this thread in the table.
*/
if (RT_UNLIKELY(!pEntry))
{
return VERR_SEM_LV_NOT_SIGNALLER;
}
return VINF_SUCCESS;
}
{
if (Thread == NIL_RTTHREAD)
return 0;
if (!pThread)
return VERR_INVALID_HANDLE;
return cWriteLocks;
}
{
}
{
}
{
if (Thread == NIL_RTTHREAD)
return 0;
if (!pThread)
return VERR_INVALID_HANDLE;
return cReadLocks;
}
{
}
{
}
{
if (pThread)
{
if (RTTHREAD_IS_SLEEPING(enmState))
{
if (RTTHREAD_IS_SLEEPING(enmState))
{
if (pRec)
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
case RTLOCKVALRECSHRD_MAGIC:
break;
}
}
}
}
}
return pvLock;
}
{
bool fRet = false;
if (pThread)
{
}
return fRet;
}
{
bool fRet = false;
if (hCurrentThread == NIL_RTTHREAD)
else
if (pThread)
{
if (hClass != NIL_RTLOCKVALCLASS)
{
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
case RTLOCKVALRECNEST_MAGIC:
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
}
break;
default:
break;
}
}
}
}
return fRet;
}
RTDECL(bool) RTLockValidatorHoldsLocksInSubClass(RTTHREAD hCurrentThread, RTLOCKVALCLASS hClass, uint32_t uSubClass)
{
bool fRet = false;
if (hCurrentThread == NIL_RTTHREAD)
else
if (pThread)
{
if (hClass != NIL_RTLOCKVALCLASS)
{
{
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
case RTLOCKVALRECNEST_MAGIC:
{
case RTLOCKVALRECEXCL_MAGIC:
break;
break;
}
break;
default:
break;
}
}
}
}
return fRet;
}
{
}
RTDECL(bool) RTLockValidatorIsEnabled(void)
{
return ASMAtomicUoReadBool(&g_fLockValidatorEnabled);
}
{
}
RTDECL(bool) RTLockValidatorIsQuiet(void)
{
return ASMAtomicUoReadBool(&g_fLockValidatorQuiet);
}
{
}
RTDECL(bool) RTLockValidatorMayPanic(void)
{
return ASMAtomicUoReadBool(&g_fLockValidatorMayPanic);
}