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.
1096 lines
23 KiB
1096 lines
23 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1998 - 1999
|
|
|
|
Module Name:
|
|
|
|
locks
|
|
|
|
Abstract:
|
|
|
|
This module provides the implementations of common lock objects.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
Notes:
|
|
|
|
?Notes?
|
|
|
|
--*/
|
|
|
|
#define __SUBROUTINE__
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <stdarg.h>
|
|
#include "cspUtils.h"
|
|
|
|
|
|
//
|
|
//==============================================================================
|
|
//
|
|
// CAccessLock
|
|
//
|
|
|
|
/*++
|
|
|
|
CONSTRUCTOR:
|
|
|
|
A CAccessLock provides a multiple-reader, single writer lock on a structure.
|
|
|
|
Arguments:
|
|
|
|
dwTimeout supplies a reasonable timeout value for any lock.
|
|
|
|
Remarks:
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::CAccessLock")
|
|
|
|
CAccessLock::CAccessLock(
|
|
DWORD dwTimeout)
|
|
: m_csLock(CSID_ACCESSCONTROL),
|
|
m_hSignalNoReaders(DBGT("CAccessLock No Readers Event")),
|
|
m_hSignalNoWriters(DBGT("CAccessLock No Writers Event"))
|
|
#ifdef DBG
|
|
, m_rgdwReaders()
|
|
#endif
|
|
{
|
|
m_hSignalNoReaders = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (!m_hSignalNoReaders.IsValid())
|
|
{
|
|
DWORD dwSts = m_hSignalNoReaders.GetLastError();
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Access Lock Object cannot create the No Readers signal: %1"),
|
|
dwSts);
|
|
throw dwSts; // Force a shutdown.
|
|
}
|
|
m_hSignalNoWriters = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
if (!m_hSignalNoWriters.IsValid())
|
|
{
|
|
DWORD dwSts = m_hSignalNoWriters.GetLastError();
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Access Lock Object cannot create the No Writers signal: %1"),
|
|
dwSts);
|
|
throw dwSts; // Force a shutdown.
|
|
}
|
|
m_dwOwner = 0;
|
|
m_dwReadCount = m_dwWriteCount = 0;
|
|
m_dwTimeout = dwTimeout;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
DESTRUCTOR:
|
|
|
|
This cleans up after a CAccessLock.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::~CAccessLock")
|
|
|
|
CAccessLock::~CAccessLock()
|
|
{
|
|
{
|
|
CLockWrite rwLock(this);
|
|
m_csLock.Enter(
|
|
__SUBROUTINE__,
|
|
DBGT("Closing down the CAccessLock"));
|
|
}
|
|
#ifdef DBG
|
|
{
|
|
ASSERT(0 == m_dwReadCount);
|
|
for (DWORD ix = m_rgdwReaders.Count(); ix > 0;)
|
|
{
|
|
ix -= 1;
|
|
ASSERT(0 == m_rgdwReaders[ix]);
|
|
}
|
|
}
|
|
#endif
|
|
if (m_hSignalNoReaders.IsValid())
|
|
m_hSignalNoReaders.Close();
|
|
if (m_hSignalNoWriters.IsValid())
|
|
m_hSignalNoWriters.Close();
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Wait:
|
|
|
|
Wait for the usage signal to trigger.
|
|
|
|
Arguments:
|
|
|
|
hSignal supplies the handle to use for the wait.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
This routine blocks until the usage signal fires.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::Wait")
|
|
|
|
void
|
|
CAccessLock::Wait(
|
|
HANDLE hSignal)
|
|
{
|
|
WaitForever(
|
|
hSignal,
|
|
m_dwTimeout,
|
|
DBGT("Waiting for Read/Write Lock signal (owner %2): %1"),
|
|
m_dwOwner);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Signal:
|
|
|
|
This routine signals the usage signal that the structure is available.
|
|
|
|
Arguments:
|
|
|
|
hSignal supplies the handle to be signaled.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
Errors are thrown as DWORD status codes
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::Signal")
|
|
|
|
void
|
|
CAccessLock::Signal(
|
|
HANDLE hSignal)
|
|
{
|
|
if (!SetEvent(hSignal))
|
|
{
|
|
DWORD dwSts = GetLastError();
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Access Lock Object cannot set its signal: %1"),
|
|
dwSts);
|
|
throw dwSts;
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Unsignal:
|
|
|
|
This method is used to notify other threads that the lock has been taken.
|
|
|
|
Arguments:
|
|
|
|
hSignal supplies the handle to be reset.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
Errors are thrown as DWORD status codes.
|
|
|
|
Remarks:
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/3/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::Unsignal")
|
|
|
|
void
|
|
CAccessLock::Unsignal(
|
|
HANDLE hSignal)
|
|
{
|
|
if (!ResetEvent(hSignal))
|
|
{
|
|
DWORD dwSts = GetLastError();
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Access Lock Object cannot reset its signal: %1"),
|
|
dwSts);
|
|
throw dwSts;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DBG
|
|
/*
|
|
Trivial Internal consistency check routines.
|
|
*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::NotReadLocked")
|
|
|
|
BOOL
|
|
CAccessLock::NotReadLocked(
|
|
void)
|
|
{
|
|
LockSection(&m_csLock, DBGT("Verifying Lock State"));
|
|
BOOL fReturn = TRUE;
|
|
|
|
for (DWORD ix = m_rgdwReaders.Count(); ix > 0;)
|
|
{
|
|
ix -= 1;
|
|
if ((LPVOID)GetCurrentThreadId() == m_rgdwReaders[ix])
|
|
{
|
|
fReturn = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return fReturn;
|
|
}
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::IsReadLocked")
|
|
|
|
BOOL
|
|
CAccessLock::IsReadLocked(
|
|
void)
|
|
{
|
|
return !NotReadLocked();
|
|
}
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::NotWriteLocked")
|
|
|
|
BOOL
|
|
CAccessLock::NotWriteLocked(
|
|
void)
|
|
{
|
|
LockSection(&m_csLock, DBGT("Verifying Lock state"));
|
|
return (GetCurrentThreadId() != m_dwOwner);
|
|
}
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CAccessLock::IsWriteLocked")
|
|
|
|
BOOL
|
|
CAccessLock::IsWriteLocked(
|
|
void)
|
|
{
|
|
LockSection(&m_csLock, DBGT("Verifying Lock state"));
|
|
return (GetCurrentThreadId() == m_dwOwner);
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
//==============================================================================
|
|
//
|
|
// CLockRead
|
|
//
|
|
|
|
/*++
|
|
|
|
CONSTRUCTOR:
|
|
|
|
This is the constructor for a CLockRead object. The existence of this
|
|
object forms a sharable read lock on the supplied CAccessLock object.
|
|
|
|
Arguments:
|
|
|
|
pLock supplies the CAccessLock object against which a read request is to
|
|
be posted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CLockRead::CLockRead")
|
|
|
|
CLockRead::CLockRead(
|
|
CAccessLock *pLock)
|
|
{
|
|
m_pLock = pLock;
|
|
|
|
|
|
//
|
|
// Quick check to see if we're already a writer.
|
|
//
|
|
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("Make sure we're not the writer"));
|
|
if (m_pLock->m_dwOwner == GetCurrentThreadId())
|
|
{
|
|
ASSERT(0 < m_pLock->m_dwWriteCount);
|
|
m_pLock->m_dwReadCount += 1;
|
|
ASSERT(0 < m_pLock->m_dwReadCount);
|
|
#ifdef DBG
|
|
DWORD dwCurrentThread = GetCurrentThreadId();
|
|
for (DWORD ix = 0; NULL != m_pLock->m_rgdwReaders[ix]; ix += 1);
|
|
// Empty loop body
|
|
m_pLock->m_rgdwReaders.Set(ix, (LPVOID)dwCurrentThread);
|
|
#endif
|
|
m_pLock->UnsignalNoReaders();
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We're not a writer. Acquire the read lock.
|
|
//
|
|
|
|
for (;;)
|
|
{
|
|
m_pLock->WaitOnWriters();
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("Get the read lock"));
|
|
if ((0 == m_pLock->m_dwWriteCount)
|
|
|| (m_pLock->m_dwOwner == GetCurrentThreadId()))
|
|
{
|
|
m_pLock->m_dwReadCount += 1;
|
|
ASSERT(0 < m_pLock->m_dwReadCount);
|
|
#ifdef DBG
|
|
DWORD dwCurrentThread = GetCurrentThreadId();
|
|
for (DWORD ix = 0; NULL != m_pLock->m_rgdwReaders[ix]; ix += 1);
|
|
// Empty loop body
|
|
m_pLock->m_rgdwReaders.Set(ix, (LPVOID)dwCurrentThread);
|
|
#endif
|
|
m_pLock->UnsignalNoReaders();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
DESTRUCTOR:
|
|
|
|
The CLockRead destructor frees the outstanding read lock on the CAccessLock
|
|
object.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CLockRead::~CLockRead")
|
|
|
|
CLockRead::~CLockRead()
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("Releasing the read lock"));
|
|
ASSERT(0 < m_pLock->m_dwReadCount);
|
|
m_pLock->m_dwReadCount -= 1;
|
|
#ifdef DBG
|
|
DWORD dwCurrentThread = GetCurrentThreadId();
|
|
for (DWORD ix = m_pLock->m_rgdwReaders.Count(); ix > 0;)
|
|
{
|
|
ix -= 1;
|
|
if ((LPVOID)dwCurrentThread == m_pLock->m_rgdwReaders[ix])
|
|
{
|
|
m_pLock->m_rgdwReaders.Set(ix, NULL);
|
|
break;
|
|
}
|
|
ASSERT(0 < ix);
|
|
}
|
|
#endif
|
|
if (0 == m_pLock->m_dwReadCount)
|
|
m_pLock->SignalNoReaders();
|
|
}
|
|
|
|
|
|
//
|
|
//==============================================================================
|
|
//
|
|
// CLockWrite
|
|
//
|
|
|
|
/*++
|
|
|
|
CONSTRUCTOR:
|
|
|
|
This is the constructor for a CLockWrite object. The existence of this
|
|
object forms a unshared write lock on the supplied CAccessLock object.
|
|
|
|
Arguments:
|
|
|
|
pLock supplies the CAccessLock object against which a write request is to
|
|
be posted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CLockWrite::CLockWrite")
|
|
|
|
CLockWrite::CLockWrite(
|
|
CAccessLock *pLock)
|
|
{
|
|
m_pLock = pLock;
|
|
|
|
|
|
//
|
|
// Quick check to see if we're already a writer.
|
|
//
|
|
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("See if we're already a writer"));
|
|
if (m_pLock->m_dwOwner == GetCurrentThreadId())
|
|
{
|
|
ASSERT(0 < m_pLock->m_dwWriteCount);
|
|
m_pLock->m_dwWriteCount += 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We're not a writer. Acquire the write lock.
|
|
//
|
|
|
|
for (;;)
|
|
{
|
|
m_pLock->WaitOnWriters();
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("Get the Write lock"));
|
|
if (0 == m_pLock->m_dwWriteCount)
|
|
{
|
|
ASSERT(m_pLock->NotReadLocked());
|
|
ASSERT(0 == m_pLock->m_dwOwner);
|
|
m_pLock->m_dwWriteCount += 1;
|
|
m_pLock->m_dwOwner = GetCurrentThreadId();
|
|
m_pLock->UnsignalNoWriters();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
m_pLock->WaitOnReaders();
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("See if we got the read lock"));
|
|
if (0 == m_pLock->m_dwReadCount)
|
|
break;
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
DWORD dwIndex;
|
|
for (dwIndex = m_pLock->m_rgdwReaders.Count(); dwIndex > 0;)
|
|
{
|
|
dwIndex -= 1;
|
|
if (NULL != m_pLock->m_rgdwReaders[dwIndex])
|
|
break;
|
|
ASSERT(0 < dwIndex); // No one will ever respond!
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
DESTRUCTOR:
|
|
|
|
The CLockWrite destructor frees the outstanding write lock on the
|
|
CAccessLock object.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/2/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CLockWrite::~CLockWrite")
|
|
|
|
CLockWrite::~CLockWrite()
|
|
{
|
|
LockSection(&m_pLock->m_csLock, DBGT("Releasing the write lock"));
|
|
ASSERT(0 == m_pLock->m_dwReadCount);
|
|
ASSERT(0 < m_pLock->m_dwWriteCount);
|
|
ASSERT(m_pLock->m_dwOwner == GetCurrentThreadId());
|
|
m_pLock->m_dwWriteCount -= 1;
|
|
if (0 == m_pLock->m_dwWriteCount)
|
|
{
|
|
m_pLock->m_dwOwner = 0;
|
|
m_pLock->SignalNoWriters();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//==============================================================================
|
|
//
|
|
// Support Routines
|
|
//
|
|
|
|
/*++
|
|
|
|
WaitForAnObject:
|
|
|
|
This routine performs object waiting services. It really doesn't have
|
|
anything to do with locking except that there are so many error conditions
|
|
to check for that it's more convenient to have it off in its own routine.
|
|
|
|
Arguments:
|
|
|
|
hWaitOn supplies the handle to wait on.
|
|
|
|
dwTimeout supplies the wait timeout value.
|
|
|
|
Return Value:
|
|
|
|
The error code, if any
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/19/1997
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("WaitForAnObject")
|
|
|
|
DWORD
|
|
WaitForAnObject(
|
|
HANDLE hWaitOn,
|
|
DWORD dwTimeout)
|
|
{
|
|
DWORD dwReturn = SCARD_S_SUCCESS;
|
|
DWORD dwSts;
|
|
|
|
ASSERT(INVALID_HANDLE_VALUE != hWaitOn);
|
|
ASSERT(NULL != hWaitOn);
|
|
dwSts = WaitForSingleObject(hWaitOn, dwTimeout);
|
|
switch (dwSts)
|
|
{
|
|
case WAIT_FAILED:
|
|
dwSts = GetLastError();
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Wait for object failed: %1"),
|
|
dwSts);
|
|
dwReturn = dwSts;
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Wait for object timed out"));
|
|
dwReturn = SCARD_F_WAITED_TOO_LONG;
|
|
break;
|
|
case WAIT_ABANDONED:
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Wait for object received wait abandoned"));
|
|
// That's OK, we still got it.
|
|
break;
|
|
|
|
case WAIT_OBJECT_0:
|
|
break;
|
|
|
|
default:
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("Wait for object got invalid response"));
|
|
dwReturn = SCARD_F_INTERNAL_ERROR;
|
|
}
|
|
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
WaitForObjects:
|
|
|
|
This routine is a utility to allow waiting for multiple objects. It returns
|
|
the index of the object that completed.
|
|
|
|
Arguments:
|
|
|
|
dwTimeout supplies the timeout value, in milliseconds, or INFINITE.
|
|
|
|
hObject and following supply the list of objects to wait for. This list
|
|
must be NULL terminated.
|
|
|
|
Return Value:
|
|
|
|
The number of the object completed. 1 implies the first one, 2 implies the
|
|
second one, etc.
|
|
|
|
Throws:
|
|
|
|
Errors are thrown as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 6/17/1998
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("WaitForAnyObject")
|
|
|
|
DWORD
|
|
WaitForAnyObject(
|
|
DWORD dwTimeout,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
HANDLE h, rgh[4];
|
|
DWORD dwIndex = 0, dwWait, dwErr;
|
|
|
|
va_start(ap, dwTimeout);
|
|
for (h = va_arg(ap, HANDLE); NULL != h; h = va_arg(ap, HANDLE))
|
|
{
|
|
ASSERT(dwIndex < sizeof(rgh) / sizeof(HANDLE));
|
|
ASSERT(INVALID_HANDLE_VALUE != h);
|
|
if (INVALID_HANDLE_VALUE != h)
|
|
rgh[dwIndex++] = h;
|
|
}
|
|
va_end(ap);
|
|
|
|
ASSERT(0 < dwIndex);
|
|
if (0 < dwIndex)
|
|
dwWait = WaitForMultipleObjects(dwIndex, rgh, FALSE, dwTimeout);
|
|
else
|
|
{
|
|
dwWait = WAIT_FAILED;
|
|
SetLastError(ERROR_INVALID_EVENT_COUNT);
|
|
// That's a good symbolic name, but a lousy user message.
|
|
}
|
|
|
|
switch (dwWait)
|
|
{
|
|
case WAIT_FAILED:
|
|
dwErr = GetLastError();
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("WaitForObjects failed its wait: %1"),
|
|
dwErr);
|
|
throw dwErr;
|
|
break;
|
|
|
|
case WAIT_TIMEOUT:
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("WaitForObjects timed out on its wait"));
|
|
throw (DWORD)ERROR_TIMEOUT;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(WAIT_OBJECT_0 < WAIT_ABANDONED_0);
|
|
if ((dwWait >= WAIT_ABANDONED_0)
|
|
&& (dwWait < (WAIT_ABANDONED_0 + WAIT_ABANDONED_0 - WAIT_OBJECT_0)))
|
|
{
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("WaitForObjects received a Wait Abandoned warning"));
|
|
dwIndex = dwWait - WAIT_ABANDONED_0 + 1;
|
|
}
|
|
else if ((dwWait >= WAIT_OBJECT_0) && (dwWait < WAIT_ABANDONED_0))
|
|
{
|
|
dwIndex = dwWait - WAIT_OBJECT_0 + 1;
|
|
}
|
|
else
|
|
{
|
|
CalaisWarning(
|
|
__SUBROUTINE__,
|
|
DBGT("WaitForObjects received unknown error code: %1"),
|
|
dwWait);
|
|
throw dwWait;
|
|
}
|
|
}
|
|
|
|
return dwIndex;
|
|
}
|
|
|
|
|
|
#ifdef DBG
|
|
//
|
|
// Critical Section Support.
|
|
//
|
|
// The following Classes aid in debugging Critical Section Conflicts.
|
|
//
|
|
|
|
static const TCHAR l_szUnowned[] = TEXT("<Unowned>");
|
|
CDynamicArray<CCriticalSectionObject> *CCriticalSectionObject::mg_prgCSObjects = NULL;
|
|
CRITICAL_SECTION CCriticalSectionObject::mg_csArrayLock;
|
|
static const LPCTSTR l_rgszLockList[]
|
|
= { DBGT("Service Status Critical Section"), // CSID_SERVICE_STATUS
|
|
DBGT("Lock for Calais control commands."), // CSID_CONTROL_LOCK
|
|
DBGT("Lock for server thread enumeration."), // CSID_SERVER_THREADS
|
|
DBGT("MultiEvent Critical Access Section"), // CSID_MULTIEVENT
|
|
DBGT("Mutex critical access section"), // CSID_MUTEX
|
|
DBGT("Access Lock control"), // CSID_ACCESSCONTROL
|
|
DBGT("Lock for tracing output."), // CSID_TRACEOUTPUT
|
|
NULL };
|
|
|
|
|
|
//
|
|
//==============================================================================
|
|
//
|
|
// CCriticalSectionObject
|
|
//
|
|
|
|
/*++
|
|
|
|
CONSTRUCTOR:
|
|
|
|
This method builds the critical section object and coordinates its tracking.
|
|
|
|
Arguments:
|
|
|
|
szDescription supplies a description of what this critical section object
|
|
is used for. This aids identification.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 3/19/1999
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CCriticalSectionObject::CCriticalSectionObject")
|
|
|
|
CCriticalSectionObject::CCriticalSectionObject(
|
|
DWORD dwCsid)
|
|
{
|
|
InitializeCriticalSection(&m_csLock);
|
|
m_dwCsid = dwCsid;
|
|
m_bfOwner.Set((LPCBYTE)l_szUnowned, sizeof(l_szUnowned));
|
|
m_bfComment.Set((LPCBYTE)DBGT(""), sizeof(TCHAR));
|
|
m_dwOwnerThread = 0;
|
|
m_dwRecursion = 0;
|
|
if (NULL == mg_prgCSObjects)
|
|
{
|
|
InitializeCriticalSection(&mg_csArrayLock);
|
|
CCritSect csLock(&mg_csArrayLock);
|
|
mg_prgCSObjects = new CDynamicArray<CCriticalSectionObject>;
|
|
ASSERT(NULL != mg_prgCSObjects);
|
|
m_dwArrayEntry = 0;
|
|
mg_prgCSObjects->Set(m_dwArrayEntry, this);
|
|
}
|
|
else
|
|
{
|
|
CCritSect csLock(&mg_csArrayLock);
|
|
for (m_dwArrayEntry = 0;
|
|
NULL != (*mg_prgCSObjects)[m_dwArrayEntry];
|
|
m_dwArrayEntry += 1)
|
|
; // Empty loop body
|
|
mg_prgCSObjects->Set(m_dwArrayEntry, this);
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
DESTRUCTOR:
|
|
|
|
This method cleans up the critical section object.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 3/19/1999
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CCriticalSectionObject::~CCriticalSectionObject")
|
|
|
|
CCriticalSectionObject::~CCriticalSectionObject()
|
|
{
|
|
if (0 == m_dwOwnerThread)
|
|
{
|
|
ASSERT(0 == m_dwRecursion);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(IsOwnedByMe());
|
|
ASSERT(1 == m_dwRecursion);
|
|
LeaveCriticalSection(&m_csLock);
|
|
}
|
|
{
|
|
CCritSect csLock(&mg_csArrayLock);
|
|
ASSERT(this == (*mg_prgCSObjects)[m_dwArrayEntry]);
|
|
mg_prgCSObjects->Set(m_dwArrayEntry, NULL);
|
|
}
|
|
DeleteCriticalSection(&m_csLock);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Enter:
|
|
|
|
This method enters a critical section, and tracks the owner.
|
|
|
|
Arguments:
|
|
|
|
szOwner supplies the name of the calling subroutine.
|
|
|
|
szComment supplies an additional comment to help distinguish between
|
|
multiple calls within a subroutine.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 3/19/1999
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CCriticalSectionObject::Enter")
|
|
|
|
void
|
|
CCriticalSectionObject::Enter(
|
|
LPCTSTR szOwner,
|
|
LPCTSTR szComment)
|
|
{
|
|
{
|
|
CCritSect csLock(&mg_csArrayLock);
|
|
CCriticalSectionObject *pCs;
|
|
|
|
for (DWORD dwI = mg_prgCSObjects->Count(); 0 < dwI;)
|
|
{
|
|
pCs = (*mg_prgCSObjects)[--dwI];
|
|
if (m_dwArrayEntry == dwI)
|
|
{
|
|
ASSERT(this == pCs);
|
|
continue;
|
|
}
|
|
if (NULL != pCs)
|
|
{
|
|
if (pCs->IsOwnedByMe()
|
|
&& (m_dwCsid <= pCs->m_dwCsid))
|
|
{
|
|
CalaisError(
|
|
__SUBROUTINE__,
|
|
DBGT("Potential Critical Section deadlock: Owner of %1 attempting to access %2"),
|
|
pCs->Description(),
|
|
Description());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EnterCriticalSection(&m_csLock);
|
|
if (0 == m_dwRecursion)
|
|
{
|
|
ASSERT(0 == m_dwOwnerThread);
|
|
m_dwOwnerThread = GetCurrentThreadId();
|
|
m_bfOwner.Set(
|
|
(LPCBYTE)szOwner,
|
|
(lstrlen(szOwner) + 1) * sizeof(TCHAR));
|
|
m_bfComment.Set(
|
|
(LPCBYTE)szComment,
|
|
(lstrlen(szComment) + 1) * sizeof(TCHAR));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(GetCurrentThreadId() == m_dwOwnerThread);
|
|
CalaisDebug((
|
|
DBGT("Critical Section '%s' already owned by %s (%s)\nCalled from %s (%s)\n"),
|
|
Description(),
|
|
Owner(),
|
|
Comment(),
|
|
szOwner,
|
|
szComment));
|
|
}
|
|
m_dwRecursion += 1;
|
|
ASSERT(0 < m_dwRecursion);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Leave:
|
|
|
|
This method exits a critical section.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 3/19/1999
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CCriticalSectionObject::Leave")
|
|
|
|
void
|
|
CCriticalSectionObject::Leave(
|
|
void)
|
|
{
|
|
ASSERT(0 < m_dwRecursion);
|
|
m_dwRecursion -= 1;
|
|
if (0 == m_dwRecursion)
|
|
{
|
|
m_bfOwner.Set((LPCBYTE)l_szUnowned, sizeof(l_szUnowned));
|
|
m_bfComment.Set((LPCBYTE)DBGT(""), sizeof(TCHAR));
|
|
m_dwOwnerThread = 0;
|
|
}
|
|
LeaveCriticalSection(&m_csLock);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Translate the Critical Section Id number to a descriptive string.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
The descriptive string corresponding to this critical section type.
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
?Remarks?
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 3/22/1999
|
|
|
|
--*/
|
|
#undef __SUBROUTINE__
|
|
#define __SUBROUTINE__ TEXT("CCriticalSectionObject::Description")
|
|
|
|
LPCTSTR
|
|
CCriticalSectionObject::Description(
|
|
void)
|
|
const
|
|
{
|
|
return l_rgszLockList[m_dwCsid];
|
|
}
|
|
|
|
#endif
|
|
|