ICU-3786 detect deadlock on WIN32

X-SVN-Rev: 15709
This commit is contained in:
Alan Liu 2004-06-04 16:12:21 +00:00
parent 6d43abc307
commit 3c07c3f00c

View File

@ -91,6 +91,34 @@ static UMTX gIncDecMutex = NULL;
static UBool gMutexPoolInitialized = FALSE; static UBool gMutexPoolInitialized = FALSE;
static char gMutexesInUse[MAX_MUTEXES]; static char gMutexesInUse[MAX_MUTEXES];
/* ICU_WIN32_DETECT_MUTEX_DEADLOCK
*
* On WIN32 mutexes are reentrant. This makes it difficult to debug
* deadlocking problems that show up on POSIXy platforms, where
* mutexes deadlock upon reentry. To enable checking code for all
* mutexes that duplicates the recursion checking that is always done
* for the global mutex (using gRecursionCount), define this symbol.
*
* This is for debugging purposes.
*
* This has no effect on non-WIN32 platforms, non-DEBUG builds, and
* non-ICU_USE_THREADS builds.
*
* Note: The CRITICAL_SECTION structure already has a RecursionCount
* member that can be used for this purpose, but portability to
* Win98/NT/2K needs to be tested before use. Works fine on XP.
* After portability is confirmed, the built-in RecursionCount can be
* used, and the gRecursionCountPool can be removed.
*
* Note: The checking only happens if there is no custom pMutexLockFn
* defined. Use one function, not two (don't use pMutexLockFn and
* pMutexUnlockFn) so the increment and decrement of the recursion
* count don't get out of sync. Users might set just one function,
* e.g., to perform a custom action, followed by a standard call
* to EnterCriticalSection.
*/
#define ICU_WIN32_DETECT_MUTEX_DEADLOCK
#if defined(WIN32) #if defined(WIN32)
/*------------------------------------------------------------- /*-------------------------------------------------------------
* *
@ -100,9 +128,12 @@ static char gMutexesInUse[MAX_MUTEXES];
static CRITICAL_SECTION gMutexes[MAX_MUTEXES]; static CRITICAL_SECTION gMutexes[MAX_MUTEXES];
static CRITICAL_SECTION gGlobalWinMutex; static CRITICAL_SECTION gGlobalWinMutex;
/* Detect Recursive locking of the global mutex. For debugging only. */ /* Detect recursive locking on WIN32. For debugging only. */
#if defined(_DEBUG) && (ICU_USE_THREADS==1) #if defined(_DEBUG) && (ICU_USE_THREADS==1)
static int32_t gRecursionCount = 0; #ifdef ICU_WIN32_DETECT_MUTEX_DEADLOCK
static int32_t gRecursionCountPool[MAX_MUTEXES]; /* see comments above */
#endif
static int32_t gRecursionCount = 0; /* detect global mutex locking */
#endif #endif
@ -195,6 +226,23 @@ umtx_lock(UMTX *mutex)
gRecursionCount++; /* Recursion causes deadlocks on Unixes. */ gRecursionCount++; /* Recursion causes deadlocks on Unixes. */
U_ASSERT(gRecursionCount == 1); /* Detection works on Windows. Debug problems there. */ U_ASSERT(gRecursionCount == 1); /* Detection works on Windows. Debug problems there. */
} }
#ifdef ICU_WIN32_DETECT_MUTEX_DEADLOCK
/* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
else if (pMutexLockFn == NULL) { /* see comments above */
int i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0];
U_ASSERT(i >= 0 && i < MAX_MUTEXES);
++gRecursionCountPool[i];
U_ASSERT(gRecursionCountPool[i] == 1); /* !Detect Deadlock! */
/* This works and is fast, but needs testing on Win98/NT/2K.
See comments above. [alan]
U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
(CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
*/
}
#endif
#endif /*_DEBUG*/ #endif /*_DEBUG*/
} }
@ -220,6 +268,24 @@ umtx_unlock(UMTX* mutex)
gRecursionCount--; gRecursionCount--;
U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */ U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */
} }
#ifdef ICU_WIN32_DETECT_MUTEX_DEADLOCK
/* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
else if (pMutexLockFn == NULL) { /* see comments above */
int i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0];
U_ASSERT(i >= 0 && i < MAX_MUTEXES);
--gRecursionCountPool[i];
U_ASSERT(gRecursionCountPool[i] == 0); /* !Detect Deadlock! */
/* This works and is fast, but needs testing on Win98/NT/2K.
Note that RecursionCount will be 1, not 0, since we haven't
left the CRITICAL_SECTION yet. See comments above. [alan]
U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
(CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
*/
}
#endif
#endif #endif
if (pMutexUnlockFn) { if (pMutexUnlockFn) {
@ -274,6 +340,9 @@ static void initGlobalMutex() {
int i; int i;
for (i=0; i<MAX_MUTEXES; i++) { for (i=0; i<MAX_MUTEXES; i++) {
InitializeCriticalSection(&gMutexes[i]); InitializeCriticalSection(&gMutexes[i]);
#ifdef ICU_WIN32_DETECT_MUTEX_DEADLOCK
gRecursionCountPool[i] = 0; /* see comments above */
#endif
} }
gMutexPoolInitialized = TRUE; gMutexPoolInitialized = TRUE;
} }
@ -383,6 +452,7 @@ umtx_destroy(UMTX *mutex) {
/* Return this mutex to the pool of available mutexes, if it came from the /* Return this mutex to the pool of available mutexes, if it came from the
* pool in the first place. * pool in the first place.
*/ */
/* TODO use pointer math here, instead of iterating! */
int i; int i;
for (i=0; i<MAX_MUTEXES; i++) { for (i=0; i<MAX_MUTEXES; i++) {
if (*mutex == &gMutexes[i]) { if (*mutex == &gMutexes[i]) {