windows-nt/Source/XPSP1/NT/net/ias/protocol/proxy/rwlock.cpp
2020-09-26 16:20:57 +08:00

138 lines
3.4 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
//
// FILE
//
// RWLock.cpp
//
// SYNOPSIS
//
// This file implements the class RWLock.
//
// MODIFICATION HISTORY
//
// 09/04/1997 Original version.
// 09/30/1998 Fix bug with recursive LockExclusive calls.
//
///////////////////////////////////////////////////////////////////////////////
#include <proxypch.h>
#include <climits>
#include <iaswin32.h>
//////////
// Large negative value used to block shared entry into the perimeter.
//////////
const LONG BLOCK_VALUE = (-LONG_MAX)/2;
RWLock::RWLock()
: sharing(0),
waiting(0),
count(&sharing),
sharedOK(NULL),
exclusiveOK(NULL)
{
bool success = false;
do
{
sharedOK = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
if (!sharedOK) { break; }
exclusiveOK = CreateSemaphore(NULL, 0, 1, NULL);
if (!exclusiveOK) { break; }
if (!InitializeCriticalSectionAndSpinCount(&exclusive, 0x80001000))
{ break; }
success = true;
} while (FALSE);
if (!success)
{
if (exclusiveOK) { CloseHandle(exclusiveOK); }
if (sharedOK) { CloseHandle(sharedOK); }
throw std::bad_alloc();
}
}
RWLock::~RWLock() throw ()
{
CloseHandle(exclusiveOK);
CloseHandle(sharedOK);
DeleteCriticalSection(&exclusive);
}
void RWLock::Lock() const 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 RWLock::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 RWLock::Unlock() const 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) ;
}
}