//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1992. // // File: credlock.cxx // // Contents: routines to manage CredLocks // // // Functions: InitCredLocks // AllocateCredLock // BlockOnCredLock // FreeCredLock // // // History: 12-Jan-94 MikeSw Created // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include "debug.hxx" //+------------------------------------------------------------------------- // // Function: InitCredLocks // // Synopsis: Initialize credential lock events // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // //-------------------------------------------------------------------------- void CCredentialList::InitCredLocks(void) { ULONG i; for (i = 0; i < MAX_CRED_LOCKS ; i++ ) { LockEvents[i].hEvent = 0; LockEvents[i].fInUse = 0; LockEvents[i].cRecursion = 0; LockEvents[i].cWaiters = 0; } (void) RtlInitializeCriticalSection(&csLocks); cLocks = 0; } //+------------------------------------------------------------------------- // // Function: AllocateCredLock // // Synopsis: Allocates and returns a pointer to a credlock. // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // //-------------------------------------------------------------------------- PCredLock CCredentialList::AllocateCredLock(void) { NTSTATUS scRet; ULONG i = 0; // // We enter the semaphore here, guarateeing that when we // exit this wait, there are enough credlocks for Tom's tests // WaitForSingleObject(hCredLockSemaphore, INFINITE); scRet = RtlEnterCriticalSection(&csLocks); if (!NT_SUCCESS(scRet)) { DebugOut((DEB_ERROR, "failed allocating a CredLock, %x\n", scRet)); return(NULL); } if (cLocks) { for (i = 0; i < cLocks ; i++ ) { if (!(LockEvents[i].fInUse & CREDLOCK_IN_USE)) { ResetEvent(LockEvents[i].hEvent); LockEvents[i].fInUse |= CREDLOCK_IN_USE; LockEvents[i].cRecursion = 0; (void) RtlLeaveCriticalSection(&csLocks); return(&LockEvents[i]); } } } // No free locks, so we create a new one. if (cLocks == MAX_CRED_LOCKS) { DebugOut((DEB_ERROR, "Ran out of CredLocks?\n")); (void) RtlLeaveCriticalSection(&csLocks); return(NULL); } // This entails creating an event that is anonymous, auto-reset, // initally not signalled. LockEvents[cLocks].hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (LockEvents[cLocks].hEvent == NULL) { DebugOut((DEB_ERROR, "Failed creating event, %d\n", GetLastError())); } else { i = cLocks; LockEvents[cLocks++].fInUse = CREDLOCK_IN_USE; } (void) RtlLeaveCriticalSection(&csLocks); return(&LockEvents[i]); } //+------------------------------------------------------------------------- // // Function: FreeCredLock // // Synopsis: Releases a credlock // // Effects: // // Arguments: // // Requires: // // Returns: TRUE - Remove the lock record from the credential record // FALSE - Threads waiting; don't remove from cred // // Notes: // //-------------------------------------------------------------------------- BOOLEAN CCredentialList::FreeCredLock( PCredLock pLock, BOOLEAN fFreeCred) { NTSTATUS Status; BOOL bRet = TRUE; Status = RtlEnterCriticalSection(&csLocks); DsysAssert( NT_SUCCESS( Status ) ); // // The trick: If there is a waiter, then the lock is *not* free // and we set the flag saying the credential is pending delete, so // their Lock will fail. // if (pLock->cWaiters) { SetEvent(pLock->hEvent); bRet = FALSE; // // If the credential has been freed, mark that too // if (fFreeCred) { pLock->fInUse |= CREDLOCK_FREE_PENDING; } } else { pLock->fInUse &= ~CREDLOCK_IN_USE; } // And clear up the thread id field: pLock->dwThread = 0; (void) RtlLeaveCriticalSection(&csLocks); // // This is true if the lock has actually been freed // back to the credlock pool. // if (bRet) { ReleaseSemaphore(hCredLockSemaphore, 1, NULL); } return(bRet); } //+------------------------------------------------------------------------- // // Function: BlockOnCredLock // // Synopsis: Blocks a thread on a credlock. // // Effects: // // Arguments: // // Requires: credentials be blocked for write // // Returns: // // Notes: // //-------------------------------------------------------------------------- BOOLEAN CCredentialList::BlockOnCredLock(PCredLock pLock) { NTSTATUS Status; DWORD WaitRet; BOOLEAN bRet = TRUE; Status = RtlEnterCriticalSection(&csLocks); DsysAssertMsg(Status == STATUS_SUCCESS, "Critical section csLocks") ; DebugOut((DEB_TRACE,"Blocking for lock on thread 0x%x\n",pLock->dwThread)); #if DBG if (pLock->cWaiters < 4) pLock->WaiterIds[pLock->cWaiters++] = GetCurrentThreadId(); else pLock->cWaiters++; #else pLock->cWaiters++; #endif (void) RtlLeaveCriticalSection(&csLocks); // // Unlock the list, so that other threads can unlock the credentials // rCredentials.Release(); // // Wait for 100s, or until signalled: // WaitRet = WaitForSingleObject(pLock->hEvent, 600000L); // // Reacquire exclusive access to the credlock // RtlEnterCriticalSection(&csLocks); if (WaitRet) { if (WaitRet == WAIT_TIMEOUT) { #if DBG DebugOut((DEB_ERROR, "Timeout waiting for credlock %x (600s)\n", pLock)); DebugOut((DEB_ERROR, "Thread 0x%x still owns the lock, revoking\n", pLock->dwThread)); pLock->dwThread = 0; #endif // DBG // // Blow away any recursion // pLock->cRecursion = 0; } } #if DBG // // Remove our thread ID from the waiter list: // { int i; for (i = 0 ; i < 4 ; i++ ) { if (pLock->WaiterIds[i] == GetCurrentThreadId()) { int j; for (j = i; j < 4 - 1 ; j++ ) { pLock->WaiterIds[j] = pLock->WaiterIds[j+1]; } pLock->WaiterIds[4 - 1] = 0; } } } #endif // DBG pLock->cWaiters--; if (pLock->fInUse & CREDLOCK_FREE_PENDING) { if (pLock->cWaiters == 0) { pLock->fInUse &= ~(CREDLOCK_IN_USE | CREDLOCK_FREE_PENDING); } bRet = FALSE; } // // Done partying with locks // RtlLeaveCriticalSection(&csLocks); // // If the credlock needs to be freed, signal the semaphore to // put it back in the pool. // if (!bRet && (pLock->cWaiters == 0)) { ReleaseSemaphore(hCredLockSemaphore, 1, NULL); } // // Regain exclusive access to the credential list // rCredentials.GetWrite(); return(bRet); }