/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1998, Microsoft Corp. All rights reserved. // // FILE // // Perimeter.cpp // // SYNOPSIS // // This file implements the class Perimeter. // // MODIFICATION HISTORY // // 09/04/1997 Original version. // 09/30/1998 Fix bug with recursive LockExclusive calls. // /////////////////////////////////////////////////////////////////////////////// #include #include #include ////////// // Large negative value used to block shared entry into the perimeter. ////////// const LONG BLOCK_VALUE = (-LONG_MAX)/2; Perimeter::Perimeter() throw () : sharing(0), waiting(0), count(&sharing) { InitializeCriticalSection(&exclusive); sharedOK = CreateSemaphore(NULL, 0, LONG_MAX, NULL); exclusiveOK = CreateSemaphore(NULL, 0, 1, NULL); } Perimeter::~Perimeter() throw () { CloseHandle(exclusiveOK); CloseHandle(sharedOK); DeleteCriticalSection(&exclusive); } void Perimeter::Lock() throw () { // If this is less than zero, then an exlusive thread must have inserted // the BLOCK_VALUE, so ... if (InterlockedIncrement(count) <= 0) { // ... we have to wait until he's done. WaitForSingleObject(sharedOK, INFINITE); } } void Perimeter::LockExclusive() throw () { // This limits exclusive access to a single thread. EnterCriticalSection(&exclusive); // The first time through we have to wait for the sharers to finish. if (exclusive.RecursionCount == 1) { // Block any new shared threads. waiting = BLOCK_VALUE; InterlockedExchangePointer((PVOID *)&count, &waiting); // Find out how many shared threads are already in the perimeter ... LONG sharingNow = InterlockedExchangeAdd(&sharing, BLOCK_VALUE); if (sharingNow > 0) { // ... and wait until they're done. WaitForSingleObject(exclusiveOK, INFINITE); } // At this point there is no one left inside the perimeter. sharing = 0; } } void Perimeter::Unlock() throw () { // If sharing is zero, we must be an exclusive thread. if (!sharing) { // Are we about to release our last lock ? if (exclusive.RecursionCount == 1) { // Allow any new shared access attempts. InterlockedExchangePointer((PVOID *)&count, &sharing); // Find out how many threads are waiting on the semaphore ... LONG waitingNow = waiting - BLOCK_VALUE; if (waitingNow > 0) { // ... and let them go. InterlockedExchangeAdd(count, waitingNow); ReleaseSemaphore(sharedOK, waitingNow, NULL); } } // Release the exclusive lock. LeaveCriticalSection(&exclusive); } else if (InterlockedDecrement(&sharing) == BLOCK_VALUE) { // If we end up here, we must have been the last shared thread out of // the perimeter while an exlusive thread is waiting, so wake him up. ReleaseSemaphore(exclusiveOK, 1, NULL) ; } }