From c9d655b9a6edf02fd92455961445f239f4667821 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Sun, 21 Aug 2011 00:38:24 -0400 Subject: [PATCH] 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 Reviewed-by: Jeffrey Altman Tested-by: Jeffrey Altman --- src/WINNT/client_osi/osibasel.c | 91 ++++-- src/WINNT/client_osi/osibasel.h | 9 +- src/WINNT/client_osi/osisleep.c | 557 ++++++++++++++++---------------- src/WINNT/client_osi/osisleep.h | 22 +- src/WINNT/client_osi/osistatl.c | 8 +- src/WINNT/client_osi/osistatl.h | 2 +- 6 files changed, 367 insertions(+), 322 deletions(-) diff --git a/src/WINNT/client_osi/osibasel.c b/src/WINNT/client_osi/osibasel.c index 40d1207ae7..d6916e3a6f 100644 --- a/src/WINNT/client_osi/osibasel.c +++ b/src/WINNT/client_osi/osibasel.c @@ -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(); } - 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; diff --git a/src/WINNT/client_osi/osibasel.h b/src/WINNT/client_osi/osibasel.h index 73679dd78d..9ef6b90519 100644 --- a/src/WINNT/client_osi/osibasel.h +++ b/src/WINNT/client_osi/osibasel.h @@ -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 */ diff --git a/src/WINNT/client_osi/osisleep.c b/src/WINNT/client_osi/osisleep.c index aa1ded3c5f..a8109363b1 100644 --- a/src/WINNT/client_osi/osisleep.c +++ b/src/WINNT/client_osi/osisleep.c @@ -125,63 +125,63 @@ void osi_FreeSleepInfo(osi_sleepInfo_t *ap) /* allocate a new sleep structure from the free list */ osi_sleepInfo_t *osi_AllocSleepInfo() { - osi_sleepInfo_t *ap; + osi_sleepInfo_t *ap; - EnterCriticalSection(&osi_sleepInfoAllocCS); - if (!(ap = osi_sleepInfoFreeListp)) { - ap = (osi_sleepInfo_t *) malloc(sizeof(osi_sleepInfo_t)); - ap->sema = CreateSemaphore(NULL, 0, 65536, (char *) 0); - osi_sleepInfoAllocs++; - } - else { - osi_sleepInfoFreeListp = (osi_sleepInfo_t *) ap->q.nextp; - osi_sleepInfoCount--; - } - ap->tid = GetCurrentThreadId(); - ap->states = 0; /* not signalled yet */ - LeaveCriticalSection(&osi_sleepInfoAllocCS); + EnterCriticalSection(&osi_sleepInfoAllocCS); + if (!(ap = osi_sleepInfoFreeListp)) { + ap = (osi_sleepInfo_t *) malloc(sizeof(osi_sleepInfo_t)); + ap->sema = CreateSemaphore(NULL, 0, 65536, (char *) 0); + osi_sleepInfoAllocs++; + } + else { + osi_sleepInfoFreeListp = (osi_sleepInfo_t *) ap->q.nextp; + osi_sleepInfoCount--; + } + ap->tid = GetCurrentThreadId(); + ap->states = 0; /* not signalled yet */ + LeaveCriticalSection(&osi_sleepInfoAllocCS); - return ap; + return ap; } int osi_Once(osi_once_t *argp) { - long i; + long i; - while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) { - Sleep(0); - } + while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) { + Sleep(0); + } - if (argp->done == 0) { - argp->done = 1; - return 1; - } + if (argp->done == 0) { + argp->done = 1; + return 1; + } - /* otherwise we've already been initialized, so clear lock and return */ - InterlockedExchange(&argp->atomic, 0); - return 0; + /* otherwise we've already been initialized, so clear lock and return */ + InterlockedExchange(&argp->atomic, 0); + return 0; } void osi_EndOnce(osi_once_t *argp) { - InterlockedExchange(&argp->atomic, 0); + InterlockedExchange(&argp->atomic, 0); } int osi_TestOnce(osi_once_t *argp) { - long localDone; - long i; + long localDone; + long i; - while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) { - Sleep(0); - } + while ((i=InterlockedExchange(&argp->atomic, 1)) != 0) { + Sleep(0); + } - localDone = argp->done; + localDone = argp->done; - /* drop interlock */ - InterlockedExchange(&argp->atomic, 0); + /* drop interlock */ + InterlockedExchange(&argp->atomic, 0); - return (localDone? 0 : 1); + return (localDone? 0 : 1); } /* Initialize the package, should be called while single-threaded. @@ -190,74 +190,75 @@ int osi_TestOnce(osi_once_t *argp) */ void osi_Init(void) { - int i; - static osi_once_t once; - unsigned long remainder; /* for division output */ - osi_fdType_t *typep; - SYSTEMTIME sysTime; - FILETIME fileTime; - osi_hyper_t bootTime; + int i; + static osi_once_t once; + unsigned long remainder; /* for division output */ + osi_fdType_t *typep; + SYSTEMTIME sysTime; + FILETIME fileTime; + osi_hyper_t bootTime; - /* check to see if already initialized; if so, claim success */ - if (!osi_Once(&once)) return; + /* check to see if already initialized; if so, claim success */ + if (!osi_Once(&once)) + return; - /* setup boot time values */ - GetSystemTime(&sysTime); - SystemTimeToFileTime(&sysTime, &fileTime); + /* setup boot time values */ + GetSystemTime(&sysTime); + SystemTimeToFileTime(&sysTime, &fileTime); - /* change the base of the time so it won't be negative for a long time */ - fileTime.dwHighDateTime -= 28000000; + /* change the base of the time so it won't be negative for a long time */ + fileTime.dwHighDateTime -= 28000000; - bootTime.HighPart = fileTime.dwHighDateTime; - bootTime.LowPart = fileTime.dwLowDateTime; - /* now, bootTime is in 100 nanosecond units, and we'd really rather - * have it in 1 second units, units 10,000,000 times bigger. - * So, we divide. - */ - bootTime = ExtendedLargeIntegerDivide(bootTime, 10000000, &remainder); - osi_bootTime = bootTime.LowPart; + bootTime.HighPart = fileTime.dwHighDateTime; + bootTime.LowPart = fileTime.dwLowDateTime; + /* now, bootTime is in 100 nanosecond units, and we'd really rather + * have it in 1 second units, units 10,000,000 times bigger. + * So, we divide. + */ + bootTime = ExtendedLargeIntegerDivide(bootTime, 10000000, &remainder); + osi_bootTime = bootTime.LowPart; - /* initialize thread-local storage for sleep Info structures */ - osi_SleepSlot = TlsAlloc(); + /* initialize thread-local storage for sleep Info structures */ + osi_SleepSlot = TlsAlloc(); - /* init FD system */ - osi_InitFD(); + /* init FD system */ + osi_InitFD(); - /* initialize critical regions and semaphores */ - for(i=0;irefCount = 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; LeaveCriticalSection(releasep); - /* now wait for the signal */ - while(1) { - /* wait */ - code = WaitForSingleObject(sp->sema, - /* timeout */ INFINITE); + /* now wait for the signal */ + while(1) { + /* wait */ + code = WaitForSingleObject(sp->sema, + /* timeout */ INFINITE); - /* if the reason for the wakeup was that we were signalled, - * break out, otherwise try again, since the semaphore count is - * decreased only when we get WAIT_OBJECT_0 back. - */ - if (code == WAIT_OBJECT_0) break; - } /* while we're waiting */ - - /* we're the only one who should be looking at or changing this - * structure after it gets signalled. Sema sp->sema isn't signalled - * any longer after we're back from WaitForSingleObject, so we can - * free this element directly. + /* if the reason for the wakeup was that we were signalled, + * break out, otherwise try again, since the semaphore count is + * decreased only when we get WAIT_OBJECT_0 back. */ - osi_assert(sp->states & OSI_SLEEPINFO_SIGNALLED); + if (code == WAIT_OBJECT_0) break; + } /* while we're waiting */ - osi_FreeSleepInfo(sp); + /* we're the only one who should be looking at or changing this + * structure after it gets signalled. Sema sp->sema isn't signalled + * any longer after we're back from WaitForSingleObject, so we can + * free this element directly. + */ + osi_assert(sp->states & OSI_SLEEPINFO_SIGNALLED); - /* reobtain, since caller commonly needs it */ - EnterCriticalSection(releasep); + osi_FreeSleepInfo(sp); + + /* reobtain, since caller commonly needs it */ + EnterCriticalSection(releasep); } /* must be called with a critical section held that guards the turnstile @@ -311,29 +313,29 @@ void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, CRITICAL_SECTI */ void osi_TSignal(osi_turnstile_t *turnp) { - osi_sleepInfo_t *sp; + osi_sleepInfo_t *sp; - if (!turnp->lastp) - return; + if (!turnp->lastp) + return; - sp = turnp->lastp; - turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&sp->q); - osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q); - sp->states |= OSI_SLEEPINFO_SIGNALLED; - ReleaseSemaphore(sp->sema, 1, (long *) 0); + sp = turnp->lastp; + turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&sp->q); + osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q); + sp->states |= OSI_SLEEPINFO_SIGNALLED; + ReleaseSemaphore(sp->sema, 1, (long *) 0); } /* like TSignal, only wake *everyone* */ void osi_TBroadcast(osi_turnstile_t *turnp) { - osi_sleepInfo_t *sp; + osi_sleepInfo_t *sp; - while(sp = turnp->lastp) { - turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&sp->q); - osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q); - sp->states |= OSI_SLEEPINFO_SIGNALLED; - ReleaseSemaphore(sp->sema, 1, (long *) 0); - } /* while someone's still asleep */ + while(sp = turnp->lastp) { + turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&sp->q); + osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &sp->q); + sp->states |= OSI_SLEEPINFO_SIGNALLED; + ReleaseSemaphore(sp->sema, 1, (long *) 0); + } /* while someone's still asleep */ } /* special turnstile signal for mutexes and locks. Wakes up only those who @@ -352,64 +354,71 @@ void osi_TBroadcast(osi_turnstile_t *turnp) */ void osi_TSignalForMLs(osi_turnstile_t *turnp, int stillHaveReaders, CRITICAL_SECTION *csp) { - osi_sleepInfo_t *tsp; /* a temp */ - osi_sleepInfo_t *nsp; /* a temp */ - osi_queue_t *wakeupListp; /* list of dudes to wakeup after dropping lock */ - int wokeReader; - unsigned short *sp; - unsigned char *cp; + osi_sleepInfo_t *tsp; /* a temp */ + osi_sleepInfo_t *nsp; /* a temp */ + osi_queue_t *wakeupListp; /* list of dudes to wakeup after dropping lock */ + int wokeReader; + unsigned short *sp; + unsigned char *cp; - wokeReader = stillHaveReaders; - wakeupListp = NULL; - while(tsp = turnp->lastp) { - /* look at each sleepInfo until we find someone we're not supposed to - * wakeup. - */ - if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) { - if (wokeReader) break; - } - else wokeReader = 1; + wokeReader = stillHaveReaders; + wakeupListp = NULL; + while(tsp = turnp->lastp) { + /* look at each sleepInfo until we find someone we're not supposed to + * wakeup. + */ + if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) { + if (wokeReader) + break; + } + 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 - * the crit sec. - */ - turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&tsp->q); - osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &tsp->q); + /* 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 + * the crit sec. + */ + turnp->lastp = (osi_sleepInfo_t *) osi_QPrev(&tsp->q); + osi_QRemoveHT((osi_queue_t **) &turnp->firstp, (osi_queue_t **) &turnp->lastp, &tsp->q); - /* do the patching required for lock obtaining */ - if (tsp->waitFor & OSI_SLEEPINFO_W4WRITE) { - cp = (void *) tsp->value; - (*cp) |= OSI_LOCKFLAG_EXCL; - } - else if (tsp->waitFor & OSI_SLEEPINFO_W4READ) { - sp = (void *) tsp->value; - (*sp)++; - } - - /* and add to our own list */ - tsp->q.nextp = wakeupListp; - wakeupListp = &tsp->q; - - /* now if we woke a writer, we're done, since it is pointless - * to wake more than one writer. - */ - if (!wokeReader) break; + /* do the patching required for lock obtaining */ + 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; } - /* hit end, or found someone we're not supposed to wakeup */ - if (csp) LeaveCriticalSection(csp); + /* and add to our own list */ + tsp->q.nextp = wakeupListp; + wakeupListp = &tsp->q; + + /* now if we woke a writer, we're done, since it is pointless + * to wake more than one writer. + */ + if (!wokeReader) + break; + } + + /* hit end, or found someone we're not supposed to wakeup */ + if (csp) + LeaveCriticalSection(csp); /* finally, wakeup everyone we found. Don't free things since the sleeper * will free the sleepInfo structure. */ for(tsp = (osi_sleepInfo_t *) wakeupListp; tsp; tsp = nsp) { - /* pull this out first, since *tsp *could* get freed immediately - * after the ReleaseSemaphore, if a context swap occurs. - */ - nsp = (osi_sleepInfo_t *) tsp->q.nextp; - tsp->states |= OSI_SLEEPINFO_SIGNALLED; - ReleaseSemaphore(tsp->sema, 1, (long *) 0); + /* pull this out first, since *tsp *could* get freed immediately + * after the ReleaseSemaphore, if a context swap occurs. + */ + nsp = (osi_sleepInfo_t *) tsp->q.nextp; + tsp->states |= OSI_SLEEPINFO_SIGNALLED; + ReleaseSemaphore(tsp->sema, 1, (long *) 0); } } @@ -492,39 +501,39 @@ void osi_WakeupSpin(LONG_PTR sleepValue) void osi_Sleep(LONG_PTR sleepVal) { - CRITICAL_SECTION *csp; + CRITICAL_SECTION *csp; - /* may as well save some code by using SleepSched again */ - csp = &osi_baseAtomicCS[0]; - EnterCriticalSection(csp); - osi_SleepSpin(sleepVal, csp); + /* may as well save some code by using SleepSched again */ + csp = &osi_baseAtomicCS[0]; + EnterCriticalSection(csp); + osi_SleepSpin(sleepVal, csp); } void osi_Wakeup(LONG_PTR sleepVal) { - /* how do we do osi_Wakeup on a per-lock package type? */ + /* how do we do osi_Wakeup on a per-lock package type? */ - osi_WakeupSpin(sleepVal); + osi_WakeupSpin(sleepVal); } long osi_SleepFDCreate(osi_fdType_t *fdTypep, osi_fd_t **outpp) { - osi_sleepFD_t *cp; + osi_sleepFD_t *cp; - cp = (osi_sleepFD_t *)malloc(sizeof(*cp)); - memset((void *) cp, 0, sizeof(*cp)); - cp->idx = 0; - cp->sip = NULL; + cp = (osi_sleepFD_t *)malloc(sizeof(*cp)); + memset((void *) cp, 0, sizeof(*cp)); + cp->idx = 0; + cp->sip = NULL; - /* done */ - *outpp = &cp->fd; - return 0; + /* done */ + *outpp = &cp->fd; + return 0; } long osi_SleepFDClose(osi_fd_t *cp) { - free((void *) cp); - return 0; + free((void *) cp); + return 0; } /* called with osi_sleepFDCS locked; returns with same, so that @@ -533,52 +542,55 @@ long osi_SleepFDClose(osi_fd_t *cp) */ void osi_AdvanceSleepFD(osi_sleepFD_t *cp) { - int idx; /* index we're dealing with */ - int oidx; /* index we locked */ - osi_sleepInfo_t *sip; - osi_sleepInfo_t *nsip; + int idx; /* index we're dealing with */ + int oidx; /* index we locked */ + osi_sleepInfo_t *sip; + osi_sleepInfo_t *nsip; - idx = 0; /* so we go around once safely */ - sip = NULL; - while(idx < OSI_SLEEPHASHSIZE) { - /* cp->sip should be held */ - idx = cp->idx; - EnterCriticalSection(&osi_critSec[idx]); - oidx = idx; /* remember original index; that's the one we locked */ + idx = 0; /* so we go around once safely */ + sip = NULL; + while(idx < OSI_SLEEPHASHSIZE) { + /* cp->sip should be held */ + idx = cp->idx; + EnterCriticalSection(&osi_critSec[idx]); + oidx = idx; /* remember original index; that's the one we locked */ - /* if there's a sleep info structure in the FD, it should be held; it - * is the one we just processed, so we want to move on to the next. - * If not, then we want to process the chain in the bucket idx points - * to. - */ - if ((sip = cp->sip) == NULL) { - sip = osi_sleepers[idx]; - if (!sip) idx++; - else sip->refCount++; - } - else { - /* it is safe to release the current sleep info guy now - * since we hold the bucket lock. Pull next guy out first, - * since if sip is deleted, Release will move him into - * free list. - */ - nsip = (osi_sleepInfo_t *) sip->q.nextp; - osi_ReleaseSleepInfo(sip); - sip = nsip; + /* if there's a sleep info structure in the FD, it should be held; it + * is the one we just processed, so we want to move on to the next. + * If not, then we want to process the chain in the bucket idx points + * to. + */ + if ((sip = cp->sip) == NULL) { + sip = osi_sleepers[idx]; + if (!sip) idx++; + else sip->refCount++; + } + else { + /* it is safe to release the current sleep info guy now + * since we hold the bucket lock. Pull next guy out first, + * since if sip is deleted, Release will move him into + * free list. + */ + nsip = (osi_sleepInfo_t *) sip->q.nextp; + osi_ReleaseSleepInfo(sip); + sip = nsip; - if (sip) sip->refCount++; - else idx++; - } - cp->idx = idx; - cp->sip = sip; - LeaveCriticalSection(&osi_critSec[oidx]); + if (sip) + sip->refCount++; + else + idx++; + } + cp->idx = idx; + cp->sip = sip; + LeaveCriticalSection(&osi_critSec[oidx]); - /* now, if we advanced to a new sleep info structure, we're - * done, otherwise we continue and look at the next hash bucket - * until we're out of them. - */ - if (sip) break; - } + /* now, if we advanced to a new sleep info structure, we're + * done, otherwise we continue and look at the next hash bucket + * until we're out of them. + */ + if (sip) + break; + } } @@ -620,7 +632,7 @@ long osi_SleepFDGetInfo(osi_fd_t *ifdp, osi_remGetInfoParms_t *parmsp) /* finally, DLL-specific code for NT */ BOOL APIENTRY DLLMain(HANDLE inst, DWORD why, char *reserved) { - return 1; + return 1; } /* some misc functions for setting hash table sizes */ @@ -628,48 +640,51 @@ BOOL APIENTRY DLLMain(HANDLE inst, DWORD why, char *reserved) /* return true iff x is prime */ int osi_IsPrime(unsigned long x) { - unsigned long c; + unsigned long c; - /* even numbers aren't prime */ - if ((x & 1) == 0 && x != 2) return 0; + /* even numbers aren't prime */ + if ((x & 1) == 0 && x != 2) return 0; - for(c = 3; c x) return 1; - } + /* see if we've gone far enough; only have to compute until + * square root of x. + */ + if (c*c > x) + return 1; + } - /* probably never get here */ - return 1; + /* probably never get here */ + return 1; } /* return first prime number less than or equal to x */ unsigned long osi_PrimeLessThan(unsigned long x) { - unsigned long c; + unsigned long c; - for(c = x; c > 1; c--) { - if (osi_IsPrime(c)) return c; - } + for(c = x; c > 1; c--) { + if (osi_IsPrime(c)) + return c; + } - /* ever reached? */ - return 1; + /* ever reached? */ + return 1; } /* return the # of seconds since some fixed date */ unsigned long osi_GetBootTime(void) { - return osi_bootTime; + return osi_bootTime; } static int (*notifFunc)(char *, char *, long) = NULL; void osi_InitPanic(void *anotifFunc) { - notifFunc = anotifFunc; + notifFunc = anotifFunc; } void osi_panic(char *msgp, char *filep, long line) @@ -712,26 +727,26 @@ time_t osi_Time(void) /* get time in seconds since some relatively recent time */ void osi_GetTime(long *timesp) { - FILETIME fileTime; - SYSTEMTIME sysTime; - unsigned long remainder; - LARGE_INTEGER bootTime; + FILETIME fileTime; + SYSTEMTIME sysTime; + unsigned long remainder; + LARGE_INTEGER bootTime; - /* setup boot time values */ - GetSystemTime(&sysTime); - SystemTimeToFileTime(&sysTime, &fileTime); + /* setup boot time values */ + GetSystemTime(&sysTime); + SystemTimeToFileTime(&sysTime, &fileTime); - /* change the base of the time so it won't be negative for a long time */ - fileTime.dwHighDateTime -= 28000000; + /* change the base of the time so it won't be negative for a long time */ + fileTime.dwHighDateTime -= 28000000; - bootTime.HighPart = fileTime.dwHighDateTime; - bootTime.LowPart = fileTime.dwLowDateTime; - /* now, bootTime is in 100 nanosecond units, and we'd really rather - * have it in 1 microsecond units, units 10 times bigger. - * So, we divide. - */ - bootTime = ExtendedLargeIntegerDivide(bootTime, 10, &remainder); - bootTime = ExtendedLargeIntegerDivide(bootTime, 1000000, &remainder); - timesp[0] = bootTime.LowPart; /* seconds */ - timesp[1] = remainder; /* microseconds */ + bootTime.HighPart = fileTime.dwHighDateTime; + bootTime.LowPart = fileTime.dwLowDateTime; + /* now, bootTime is in 100 nanosecond units, and we'd really rather + * have it in 1 microsecond units, units 10 times bigger. + * So, we divide. + */ + bootTime = ExtendedLargeIntegerDivide(bootTime, 10, &remainder); + bootTime = ExtendedLargeIntegerDivide(bootTime, 1000000, &remainder); + timesp[0] = bootTime.LowPart; /* seconds */ + timesp[1] = remainder; /* microseconds */ } diff --git a/src/WINNT/client_osi/osisleep.h b/src/WINNT/client_osi/osisleep.h index 4c7fb9b891..0c3fe46798 100644 --- a/src/WINNT/client_osi/osisleep.h +++ b/src/WINNT/client_osi/osisleep.h @@ -25,14 +25,15 @@ #define OSI_SLEEPINFO_W4READ 1 /* waiting for a read lock */ #define OSI_SLEEPINFO_W4WRITE 2 /* waiting for a write lock */ typedef struct osi_sleepInfo { - osi_queue_t q; - LONG_PTR value; /* sleep value when in a sleep queue, patch addr for turnstiles */ - size_t tid; /* thread ID of sleeper */ - EVENT_HANDLE sema; /* semaphore for this entry */ - unsigned short states; /* states bits */ - unsigned short idx; /* sleep hash table we're in, if in hash */ - unsigned short waitFor; /* what are we waiting for; used for bulk wakeups */ - unsigned long refCount; /* reference count from FDs */ + 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 */ + unsigned short idx; /* sleep hash table we're in, if in hash */ + unsigned short waitFor; /* what are we waiting for; used for bulk wakeups */ + unsigned long refCount; /* reference count from FDs */ } osi_sleepInfo_t; /* first guy is the most recently added process */ @@ -144,8 +145,9 @@ void osi_panic(char *, char *, long); time_t osi_Time(void); -extern void osi_TWait(osi_turnstile_t *turnp, int waitFor, void *patchp, - Crit_Sec *releasep); +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); diff --git a/src/WINNT/client_osi/osistatl.c b/src/WINNT/client_osi/osistatl.c index 1f42042242..d11f5d7ed1 100644 --- a/src/WINNT/client_osi/osistatl.c +++ b/src/WINNT/client_osi/osistatl.c @@ -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); } diff --git a/src/WINNT/client_osi/osistatl.h b/src/WINNT/client_osi/osistatl.h index b81a3a8647..89e81c55dc 100644 --- a/src/WINNT/client_osi/osistatl.h +++ b/src/WINNT/client_osi/osistatl.h @@ -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 */