/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: LOCK.CPP Abstract: Implements the generic class for obtaining read and write locks to some resource. See lock.h for all documentation. Classes defined: CLock History: a-levn 5-Sept-96 Created. 3/10/97 a-levn Fully documented --*/ #include "precomp.h" #include #include "lock.h" #include // debugging. #define PRINTF //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** CLock::CLock() : m_nReading(0), m_nWriting(0), m_nWaitingToRead(0), m_nWaitingToWrite(0) { // Initialize the critical sections // ================================ InitializeCriticalSection(&m_csAll); InitializeCriticalSection(&m_csEntering); // Create unnamed events for reading and writing // ============================================= m_hCanRead = CreateEvent(NULL, TRUE, TRUE, NULL); m_hCanWrite = CreateEvent(NULL, TRUE, TRUE, NULL); } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** CLock::~CLock() { CloseHandle(m_hCanWrite); CloseHandle(m_hCanRead); DeleteCriticalSection(&m_csAll); DeleteCriticalSection(&m_csEntering); } BOOL CLock::IsHoldingReadLock() { // Check if this thread already owns this lock // =========================================== EnterCriticalSection(&m_csAll); DWORD_PTR dwThreadId = GetCurrentThreadId(); for(int i = 0; i < m_adwReaders.Size(); i++) { if(dwThreadId == (DWORD_PTR)m_adwReaders[i]) { LeaveCriticalSection(&m_csAll); return TRUE; } } LeaveCriticalSection(&m_csAll); return FALSE; } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** int CLock::ReadLock(DWORD dwTimeout) { PRINTF("%d wants to read\n", GetCurrentThreadId()); // Check if this thread already owns this lock // =========================================== EnterCriticalSection(&m_csAll); DWORD_PTR dwThreadId = GetCurrentThreadId(); for(int i = 0; i < m_adwReaders.Size(); i++) { if(dwThreadId == (DWORD_PTR)m_adwReaders[i]) { // We already have it --- add it to the list and return // ==================================================== m_adwReaders.Add((void*)dwThreadId); m_nReading++; LeaveCriticalSection(&m_csAll); return NoError; } } // Don't have it already // ===================== LeaveCriticalSection(&m_csAll); // Get in line for getting any kind of lock (those unlocking don't go into // this line) // ======================================================================= EnterCriticalSection(&m_csEntering); // We are the only ones allowed to get any kind of lock now. Wait for the // event indicating that reading is enabled to become signaled // ====================================================================== PRINTF("%d next to enter\n", GetCurrentThreadId()); if(m_nWriting != 0) { int nRes = WaitFor(m_hCanRead, dwTimeout); if(nRes != NoError) { LeaveCriticalSection(&m_csEntering); return nRes; } } // Enter inner critical section (unlockers use it too), increment the // number of readers and disable writing. // ================================================================== PRINTF("%d got event\n", GetCurrentThreadId()); EnterCriticalSection(&m_csAll); m_nReading++; m_adwReaders.Add((void*)dwThreadId); PRINTF("Reset write\n"); ResetEvent(m_hCanWrite); PRINTF("Done\n"); // Get out of all critical sections and return // =========================================== LeaveCriticalSection(&m_csAll); LeaveCriticalSection(&m_csEntering); PRINTF("%d begins to read\n", GetCurrentThreadId()); return NoError; } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** int CLock::ReadUnlock() { PRINTF("%d wants to unlock reading\n", GetCurrentThreadId()); // Enter internal ciritcal section and decrement the number of readers // =================================================================== EnterCriticalSection(&m_csAll); m_nReading--; if(m_nReading < 0) return Failed; // Remove it from the list of threads // ================================== DWORD_PTR dwThreadId = GetCurrentThreadId(); for(int i = 0; i < m_adwReaders.Size(); i++) { if((DWORD_PTR)m_adwReaders[i] == dwThreadId) { m_adwReaders.RemoveAt(i); break; } } // If all reasders are gone, allow writers in // ========================================== if(m_nReading == 0) { PRINTF("%d is the last reader\n", GetCurrentThreadId()); PRINTF("Set write\n"); if(!SetEvent(m_hCanWrite)) { LeaveCriticalSection(&m_csAll); return Failed; } PRINTF("Done\n"); } else PRINTF("%d sees %d still reading\n", GetCurrentThreadId(), m_nReading); // Get out and return // ================== LeaveCriticalSection(&m_csAll); return NoError; } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** int CLock::WriteLock(DWORD dwTimeout) { PRINTF("%d wants to write\n", GetCurrentThreadId()); // Get in line for getting any kind of lock. Those unlocking don't use this // critical section. // ======================================================================== EnterCriticalSection(&m_csEntering); // We are the only ones allowed to get any kind of lock now // ======================================================== PRINTF("%d next to enter\n", GetCurrentThreadId()); // Wait for the event allowing writing to become signaled // ====================================================== int nRes = WaitFor(m_hCanWrite, dwTimeout); PRINTF("%d got event\n", GetCurrentThreadId()); if(nRes != NoError) { LeaveCriticalSection(&m_csEntering); return nRes; } // Enter internal critical section (unlockers use it too), increment the // number of writers (from 0 to 1) and disable both reading and writing // from now on. // ====================================================================== EnterCriticalSection(&m_csAll); m_nWriting++; PRINTF("Reset both\n"); ResetEvent(m_hCanWrite); ResetEvent(m_hCanRead); PRINTF("Done\n"); // Get out and return // ================== LeaveCriticalSection(&m_csAll); LeaveCriticalSection(&m_csEntering); PRINTF("%d begins to write\n", GetCurrentThreadId()); return NoError; } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** int CLock::WriteUnlock() { PRINTF("%d wants to release writing\n", GetCurrentThreadId()); // Enter lock determination critical section // ========================================= EnterCriticalSection(&m_csAll); m_nWriting--; if(m_nWriting < 0) return Failed; // Allow readers and writers in // ============================ PRINTF("%d released writing\n", GetCurrentThreadId()); PRINTF("Set both\n"); if(!SetEvent(m_hCanRead)) { LeaveCriticalSection(&m_csAll); return Failed; } else if(!SetEvent(m_hCanWrite)) { LeaveCriticalSection(&m_csAll); return Failed; } else { PRINTF("Done\n"); LeaveCriticalSection(&m_csAll); return NoError; } } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** int CLock::DowngradeLock() { // Enter lock determination critical section // ========================================= EnterCriticalSection(&m_csAll); if(!SetEvent(m_hCanRead)) { LeaveCriticalSection(&m_csAll); return Failed; } m_nReading++; LeaveCriticalSection(&m_csAll); return NoError; } //****************************************************************************** // // See lock.h for documentation // //****************************************************************************** int CLock::WaitFor(HANDLE hEvent, DWORD dwTimeout) { DWORD dwRes; dwRes = WaitForSingleObject(hEvent, dwTimeout); // Analyze the error code and convert to ours // ========================================== if(dwRes == WAIT_OBJECT_0) return NoError; else if(dwRes == WAIT_TIMEOUT) return TimedOut; else return Failed; }