Windows: record history of read lock threads

Maintance an array of up to 32 reader threads that have
acquired a rwlock.  Use it for debugging when things go bad.

Change-Id: I2e2e781ff355f1452e991898dfb5b91c47db9e34
Reviewed-on: http://gerrit.openafs.org/5411
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@openafs.org>
Tested-by: Jeffrey Altman <jaltman@openafs.org>
This commit is contained in:
Jeffrey Altman 2011-08-21 00:38:24 -04:00 committed by Jeffrey Altman
parent 1de4561529
commit c9d655b9a6
6 changed files with 367 additions and 322 deletions

View File

@ -177,17 +177,14 @@ void lock_ObtainWrite(osi_rwlock_t *lockp)
if (lockp->waiters > 0 || (lockp->flags & OSI_LOCKFLAG_EXCL) ||
(lockp->readers > 0)) {
lockp->waiters++;
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, csp);
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, lockp->tid, csp);
lockp->waiters--;
osi_assert(lockp->readers == 0 && (lockp->flags & OSI_LOCKFLAG_EXCL));
}
else {
} else {
/* if we're here, all clear to set the lock */
lockp->flags |= OSI_LOCKFLAG_EXCL;
lockp->tid[0] = thrd_Current();
}
lockp->tid = thrd_Current();
LeaveCriticalSection(csp);
if (lockOrderValidation) {
@ -204,6 +201,7 @@ void lock_ObtainRead(osi_rwlock_t *lockp)
CRITICAL_SECTION *csp;
osi_queue_t * lockRefH, *lockRefT;
osi_lock_ref_t *lockRefp;
DWORD tid = thrd_Current();
if ((i=lockp->type) != 0) {
if (i >= 0 && i < OSI_NLOCKTYPES)
@ -223,18 +221,21 @@ void lock_ObtainRead(osi_rwlock_t *lockp)
csp = &osi_baseAtomicCS[lockp->atomicIndex];
EnterCriticalSection(csp);
for ( i=0; i < lockp->readers; i++ ) {
osi_assertx(lockp->tid[i] != tid, "OSI_RWLOCK_READHELD");
}
/* here we have the fast lock, so see if we can obtain the real lock */
if (lockp->waiters > 0 || (lockp->flags & OSI_LOCKFLAG_EXCL)) {
lockp->waiters++;
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4READ, &lockp->readers, csp);
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4READ, &lockp->readers, lockp->tid, csp);
lockp->waiters--;
osi_assert(!(lockp->flags & OSI_LOCKFLAG_EXCL) && lockp->readers > 0);
}
else {
} else {
/* if we're here, all clear to set the lock */
lockp->readers++;
if (++lockp->readers <= OSI_RWLOCK_THREADS)
lockp->tid[lockp->readers-1] = tid;
}
LeaveCriticalSection(csp);
if (lockOrderValidation) {
@ -251,6 +252,7 @@ void lock_ReleaseRead(osi_rwlock_t *lockp)
CRITICAL_SECTION *csp;
osi_queue_t * lockRefH, *lockRefT;
osi_lock_ref_t *lockRefp;
DWORD tid = thrd_Current();
if ((i = lockp->type) != 0) {
if (i >= 0 && i < OSI_NLOCKTYPES)
@ -283,6 +285,15 @@ void lock_ReleaseRead(osi_rwlock_t *lockp)
osi_assertx(lockp->readers > 0, "read lock not held");
for ( i=0; i < lockp->readers; i++) {
if ( lockp->tid[i] == tid ) {
for ( ; i < lockp->readers - 1; i++)
lockp->tid[i] = lockp->tid[i+1];
lockp->tid[i] = 0;
break;
}
}
/* releasing a read lock can allow readers or writers */
if (--lockp->readers == 0 && !osi_TEmpty(&lockp->d.turn)) {
osi_TSignalForMLs(&lockp->d.turn, 0, csp);
@ -330,9 +341,9 @@ void lock_ReleaseWrite(osi_rwlock_t *lockp)
EnterCriticalSection(csp);
osi_assertx(lockp->flags & OSI_LOCKFLAG_EXCL, "write lock not held");
osi_assertx(lockp->tid == thrd_Current(), "write lock not held by current thread");
osi_assertx(lockp->tid[0] == thrd_Current(), "write lock not held by current thread");
lockp->tid = 0;
lockp->tid[0] = 0;
lockp->flags &= ~OSI_LOCKFLAG_EXCL;
if (!osi_TEmpty(&lockp->d.turn)) {
@ -360,14 +371,12 @@ void lock_ConvertWToR(osi_rwlock_t *lockp)
EnterCriticalSection(csp);
osi_assertx(lockp->flags & OSI_LOCKFLAG_EXCL, "write lock not held");
osi_assertx(lockp->tid == thrd_Current(), "write lock not held by current thread");
osi_assertx(lockp->tid[0] == thrd_Current(), "write lock not held by current thread");
/* convert write lock to read lock */
lockp->flags &= ~OSI_LOCKFLAG_EXCL;
lockp->readers++;
lockp->tid = 0;
if (!osi_TEmpty(&lockp->d.turn)) {
osi_TSignalForMLs(&lockp->d.turn, /* still have readers */ 1, csp);
}
@ -381,6 +390,7 @@ void lock_ConvertRToW(osi_rwlock_t *lockp)
{
long i;
CRITICAL_SECTION *csp;
DWORD tid = thrd_Current();
if ((i = lockp->type) != 0) {
if (i >= 0 && i < OSI_NLOCKTYPES)
@ -395,17 +405,26 @@ void lock_ConvertRToW(osi_rwlock_t *lockp)
osi_assertx(!(lockp->flags & OSI_LOCKFLAG_EXCL), "write lock held");
osi_assertx(lockp->readers > 0, "read lock not held");
for ( i=0; i < lockp->readers; i++) {
if ( lockp->tid[i] == tid ) {
for ( ; i < lockp->readers - 1; i++)
lockp->tid[i] = lockp->tid[i+1];
lockp->tid[i] = 0;
break;
}
}
if (--lockp->readers == 0) {
/* convert read lock to write lock */
lockp->flags |= OSI_LOCKFLAG_EXCL;
lockp->tid[0] = tid;
} else {
lockp->waiters++;
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, csp);
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, lockp->tid, csp);
lockp->waiters--;
osi_assert(lockp->readers == 0 && (lockp->flags & OSI_LOCKFLAG_EXCL));
}
lockp->tid = thrd_Current();
LeaveCriticalSection(csp);
}
@ -437,15 +456,14 @@ void lock_ObtainMutex(struct osi_mutex *lockp)
/* here we have the fast lock, so see if we can obtain the real lock */
if (lockp->waiters > 0 || (lockp->flags & OSI_LOCKFLAG_EXCL)) {
lockp->waiters++;
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, csp);
osi_TWait(&lockp->d.turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, &lockp->tid, csp);
lockp->waiters--;
osi_assert(lockp->flags & OSI_LOCKFLAG_EXCL);
}
else {
} else {
/* if we're here, all clear to set the lock */
lockp->flags |= OSI_LOCKFLAG_EXCL;
}
lockp->tid = thrd_Current();
}
LeaveCriticalSection(csp);
if (lockOrderValidation) {
@ -540,7 +558,8 @@ int lock_TryRead(struct osi_rwlock *lockp)
}
else {
/* if we're here, all clear to set the lock */
lockp->readers++;
if (++lockp->readers < OSI_RWLOCK_THREADS)
lockp->tid[lockp->readers-1] = thrd_Current();
i = 1;
}
@ -593,12 +612,10 @@ int lock_TryWrite(struct osi_rwlock *lockp)
else {
/* if we're here, all clear to set the lock */
lockp->flags |= OSI_LOCKFLAG_EXCL;
lockp->tid[0] = thrd_Current();
i = 1;
}
if (i)
lockp->tid = thrd_Current();
LeaveCriticalSection(csp);
if (lockOrderValidation && i) {
@ -646,12 +663,10 @@ int lock_TryMutex(struct osi_mutex *lockp) {
else {
/* if we're here, all clear to set the lock */
lockp->flags |= OSI_LOCKFLAG_EXCL;
lockp->tid = thrd_Current();
i = 1;
}
if (i)
lockp->tid = thrd_Current();
LeaveCriticalSection(csp);
if (lockOrderValidation && i) {
@ -669,6 +684,7 @@ void osi_SleepR(LONG_PTR sleepVal, struct osi_rwlock *lockp)
CRITICAL_SECTION *csp;
osi_queue_t * lockRefH, *lockRefT;
osi_lock_ref_t *lockRefp;
DWORD tid = thrd_Current();
if ((i = lockp->type) != 0) {
if (i >= 0 && i < OSI_NLOCKTYPES)
@ -698,6 +714,15 @@ void osi_SleepR(LONG_PTR sleepVal, struct osi_rwlock *lockp)
osi_assertx(lockp->readers > 0, "osi_SleepR: not held");
for ( i=0; i < lockp->readers; i++) {
if ( lockp->tid[i] == tid ) {
for ( ; i < lockp->readers - 1; i++)
lockp->tid[i] = lockp->tid[i+1];
lockp->tid[i] = 0;
break;
}
}
/* XXX better to get the list of things to wakeup from TSignalForMLs, and
* then do the wakeup after SleepSpin releases the low-level mutex.
*/
@ -715,6 +740,7 @@ void osi_SleepW(LONG_PTR sleepVal, struct osi_rwlock *lockp)
CRITICAL_SECTION *csp;
osi_queue_t * lockRefH, *lockRefT;
osi_lock_ref_t *lockRefp;
DWORD tid = thrd_Current();
if ((i = lockp->type) != 0) {
if (i >= 0 && i < OSI_NLOCKTYPES)
@ -745,6 +771,7 @@ void osi_SleepW(LONG_PTR sleepVal, struct osi_rwlock *lockp)
osi_assertx(lockp->flags & OSI_LOCKFLAG_EXCL, "osi_SleepW: not held");
lockp->flags &= ~OSI_LOCKFLAG_EXCL;
lockp->tid[0] = 0;
if (!osi_TEmpty(&lockp->d.turn)) {
osi_TSignalForMLs(&lockp->d.turn, 0, NULL);
}
@ -789,6 +816,7 @@ void osi_SleepM(LONG_PTR sleepVal, struct osi_mutex *lockp)
osi_assertx(lockp->flags & OSI_LOCKFLAG_EXCL, "osi_SleepM not held");
lockp->flags &= ~OSI_LOCKFLAG_EXCL;
lockp->tid = 0;
if (!osi_TEmpty(&lockp->d.turn)) {
osi_TSignalForMLs(&lockp->d.turn, 0, NULL);
}
@ -850,11 +878,8 @@ void lock_InitializeRWLock(osi_rwlock_t *mp, char *namep, unsigned short level)
/* otherwise we have the base case, which requires no special
* initialization.
*/
mp->type = 0;
mp->flags = 0;
memset(mp, 0, sizeof(osi_rwlock_t));
mp->atomicIndex = (unsigned short)(InterlockedIncrement(&atomicIndexCounter) % OSI_MUTEXHASHSIZE);
mp->readers = 0;
mp->tid = 0;
mp->level = level;
osi_TInit(&mp->d.turn);
return;

View File

@ -53,11 +53,14 @@ typedef struct osi_mutex {
*
* This type of lock has N readers or one writer.
*/
#define OSI_RWLOCK_THREADS 32
typedef struct osi_rwlock {
char type; /* for all types; type 0 uses atomic count */
char flags; /* flags for base type */
unsigned short atomicIndex; /* index into hash table for low-level sync */
DWORD tid; /* writer's tid */
DWORD tid[OSI_RWLOCK_THREADS]; /* writer's tid */
unsigned short waiters; /* waiters */
unsigned short readers; /* readers */
union {
@ -149,10 +152,10 @@ extern void osi_SetLockOrderValidation(int);
#define lock_AssertRead(x) osi_assertx(lock_GetRWLockState(x) & OSI_RWLOCK_READHELD, "!OSI_RWLOCK_READHELD")
#define lock_AssertWrite(x) osi_assertx(lock_GetRWLockState(x) & OSI_RWLOCK_WRITEHELD, "!OSI_RWLOCK_WRITEHELD")
#define lock_AssertWrite(x) osi_assertx((lock_GetRWLockState(x) & OSI_RWLOCK_WRITEHELD) && ((x)->tid[0] == thrd_Current()), "!OSI_RWLOCK_WRITEHELD")
#define lock_AssertAny(x) osi_assertx(lock_GetRWLockState(x) != 0, "!(OSI_RWLOCK_READHELD | OSI_RWLOCK_WRITEHELD)")
#define lock_AssertMutex(x) osi_assertx(lock_GetMutexState(x) & OSI_MUTEX_HELD, "!OSI_MUTEX_HELD")
#define lock_AssertMutex(x) osi_assertx((lock_GetMutexState(x) & OSI_MUTEX_HELD) && ((x)->tid == thrd_Current()), "!OSI_MUTEX_HELD")
#endif /* OPENAFS_WINNT_CLIENT_OSI_OSIBASEL_H */

View File

@ -199,7 +199,8 @@ void osi_Init(void)
osi_hyper_t bootTime;
/* check to see if already initialized; if so, claim success */
if (!osi_Once(&once)) return;
if (!osi_Once(&once))
return;
/* setup boot time values */
GetSystemTime(&sysTime);
@ -257,7 +258,7 @@ void osi_Init(void)
osi_EndOnce(&once);
}
void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, CRITICAL_SECTION *releasep)
void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, DWORD *tidp, CRITICAL_SECTION *releasep)
{
osi_sleepInfo_t *sp;
unsigned int code;
@ -273,6 +274,7 @@ void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, CRITICAL_SECTI
sp->refCount = 0;
sp->waitFor = waitFor;
sp->value = (LONG_PTR) patchp;
sp->tidp = tidp;
osi_QAddT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q);
if (!turnp->lastp)
turnp->lastp = sp;
@ -366,9 +368,11 @@ void osi_TSignalForMLs(osi_turnstile_t *turnp, int stillHaveReaders, CRITICAL_SE
* wakeup.
*/
if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) {
if (wokeReader) break;
if (wokeReader)
break;
}
else wokeReader = 1;
else
wokeReader = 1;
/* otherwise, we will wake this guy. For now, remove from this list
* and move to private one, so we can do the wakeup after releasing
@ -381,10 +385,13 @@ void osi_TSignalForMLs(osi_turnstile_t *turnp, int stillHaveReaders, CRITICAL_SE
if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) {
cp = (void *) tsp->value;
(*cp) |= OSI_LOCKFLAG_EXCL;
tsp->tidp[0] = tsp->tid;
}
else if (tsp->waitFor & OSI_SLEEPINFO_W4READ) {
sp = (void *) tsp->value;
(*sp)++;
if ((*sp) <= OSI_RWLOCK_THREADS)
tsp->tidp[(*sp)-1] = tsp->tid;
}
/* and add to our own list */
@ -394,11 +401,13 @@ void osi_TSignalForMLs(osi_turnstile_t *turnp, int stillHaveReaders, CRITICAL_SE
/* now if we woke a writer, we're done, since it is pointless
* to wake more than one writer.
*/
if (!wokeReader) break;
if (!wokeReader)
break;
}
/* hit end, or found someone we're not supposed to wakeup */
if (csp) LeaveCriticalSection(csp);
if (csp)
LeaveCriticalSection(csp);
/* finally, wakeup everyone we found. Don't free things since the sleeper
* will free the sleepInfo structure.
@ -566,8 +575,10 @@ void osi_AdvanceSleepFD(osi_sleepFD_t *cp)
osi_ReleaseSleepInfo(sip);
sip = nsip;
if (sip) sip->refCount++;
else idx++;
if (sip)
sip->refCount++;
else
idx++;
}
cp->idx = idx;
cp->sip = sip;
@ -577,7 +588,8 @@ void osi_AdvanceSleepFD(osi_sleepFD_t *cp)
* done, otherwise we continue and look at the next hash bucket
* until we're out of them.
*/
if (sip) break;
if (sip)
break;
}
}
@ -635,12 +647,14 @@ int osi_IsPrime(unsigned long x)
for(c = 3; c<x; c += 2) {
/* see if x is divisible by c */
if ((x % c) == 0) return 0; /* yup, it ain't prime */
if ((x % c) == 0)
return 0; /* yup, it ain't prime */
/* see if we've gone far enough; only have to compute until
* square root of x.
*/
if (c*c > x) return 1;
if (c*c > x)
return 1;
}
/* probably never get here */
@ -652,7 +666,8 @@ unsigned long osi_PrimeLessThan(unsigned long x) {
unsigned long c;
for(c = x; c > 1; c--) {
if (osi_IsPrime(c)) return c;
if (osi_IsPrime(c))
return c;
}
/* ever reached? */

View File

@ -27,6 +27,7 @@
typedef struct osi_sleepInfo {
osi_queue_t q;
LONG_PTR value; /* sleep value when in a sleep queue, patch addr for turnstiles */
DWORD *tidp; /* tid history */
size_t tid; /* thread ID of sleeper */
EVENT_HANDLE sema; /* semaphore for this entry */
unsigned short states; /* states bits */
@ -144,7 +145,8 @@ void osi_panic(char *, char *, long);
time_t osi_Time(void);
extern void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp,
extern void osi_TWait(osi_turnstile_t *turnp, int waitFor,
void *patchp, DWORD *tidp,
Crit_Sec *releasep);
extern void osi_TSignal(osi_turnstile_t *turnp);

View File

@ -78,7 +78,7 @@ static void lock_ObtainWriteStat(osi_rwlock_t *lockp)
lockp->waiters++;
if (!ap) ap = osi_QueueActiveInfo(&realp->qi,
OSI_ACTIVEFLAGS_WRITER | OSI_ACTIVEFLAGS_WAITER);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, csp);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, lockp->tid, csp);
lockp->waiters--;
osi_assert((lockp->flags & OSI_LOCKFLAG_EXCL) && lockp->readers == 0);
}
@ -123,7 +123,7 @@ static void lock_ObtainReadStat(osi_rwlock_t *lockp)
lockp->waiters++;
if (!ap) ap = osi_QueueActiveInfo(&realp->qi,
OSI_ACTIVEFLAGS_WAITER | OSI_ACTIVEFLAGS_READER);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4READ, &lockp->readers, csp);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4READ, &lockp->readers, lockp->tid, csp);
lockp->waiters--;
osi_assert(!(lockp->flags & OSI_LOCKFLAG_EXCL) && lockp->readers > 0);
}
@ -234,7 +234,7 @@ static void lock_ConvertRToWStat(osi_rwlock_t *lockp)
lockp->waiters++;
ap = osi_QueueActiveInfo(&realp->qi,
OSI_ACTIVEFLAGS_WRITER | OSI_ACTIVEFLAGS_WAITER);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, csp);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, lockp->tid, csp);
lockp->waiters--;
osi_assert((lockp->flags & OSI_LOCKFLAG_EXCL) && lockp->readers == 0);
@ -302,7 +302,7 @@ static void lock_ObtainMutexStat(struct osi_mutex *lockp)
if (lockp->waiters > 0 || (lockp->flags & OSI_LOCKFLAG_EXCL)) {
lockp->waiters++;
ap = osi_QueueActiveInfo(&realp->qi, OSI_ACTIVEFLAGS_WAITER);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, csp);
osi_TWait(&realp->turn, OSI_SLEEPINFO_W4WRITE, &lockp->flags, &lockp->tid, csp);
lockp->waiters--;
osi_assert(lockp->flags & OSI_LOCKFLAG_EXCL);
}

View File

@ -82,7 +82,7 @@ typedef struct osi_rwlockStat {
osi_turnstile_t turn; /* the real turnstile */
unsigned long refCount; /* so we can iterate cleanly */
short states;
DWORD tid;
DWORD tid[OSI_RWLOCK_THREADS];
/* statistics */
LARGE_INTEGER writeLockedTime; /* total time held */