You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
3.8 KiB
153 lines
3.8 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 <ias.h>
|
|
#include <climits>
|
|
#include <Perimeter.h>
|
|
|
|
//////////
|
|
// 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),
|
|
exclusiveInitialized(false),
|
|
sharedOK(0),
|
|
exclusiveOK(0)
|
|
{
|
|
}
|
|
|
|
Perimeter::~Perimeter() throw ()
|
|
{
|
|
if (exclusiveOK != 0)
|
|
{
|
|
CloseHandle(exclusiveOK);
|
|
}
|
|
|
|
if (sharedOK != 0)
|
|
{
|
|
CloseHandle(sharedOK);
|
|
}
|
|
|
|
if (exclusiveInitialized)
|
|
{
|
|
DeleteCriticalSection(&exclusive);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT Perimeter::FinalConstruct() throw ()
|
|
{
|
|
if (!InitializeCriticalSectionAndSpinCount(&exclusive, 0))
|
|
{
|
|
DWORD error = GetLastError();
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
exclusiveInitialized = true;
|
|
|
|
sharedOK = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
|
|
if (sharedOK == 0)
|
|
{
|
|
DWORD error = GetLastError();
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
|
|
exclusiveOK = CreateSemaphore(NULL, 0, 1, NULL);
|
|
if (exclusiveOK == 0)
|
|
{
|
|
DWORD error = GetLastError();
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
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) ;
|
|
}
|
|
}
|