#ifndef __ObjectLock_h__
#define __ObjectLock_h__
/////////////////////////////////////////////////////////////////////////////
// ObjectLock.h | Declaration of the TCObjectLock template class.
//
#include "AutoHandle.h"
#include "..\Inc\TCLib.h"
#include <atlconv.h>
/////////////////////////////////////////////////////////////////////////////
// Global Diagnostic Function
#ifdef _DEBUG
#ifdef _ENABLE_TRACE_LOCKS
#pragma warning(disable: 4786)
#include <map>
inline bool TCObjectLock_EnableTraceLocks(const char* pszClassName)
{
// This is not currenlty being used, due to the _ENABLE_TRACE_LOCKS
// directive not being defined, but it really appears to be quite
// thread *UNSAFE*, since this map object does not have any explicit
// thread synchronization.
typedef std::map<const char*, bool> CClassNameMap;
static CClassNameMap mapClassNames;
// First lookup the class name in the map
CClassNameMap::iterator it = mapClassNames.find(pszClassName);
if (mapClassNames.end() != it)
return (*it).second;
// Open the debugging subkey
bool bEnable = false;
const LPCTSTR pszKeyName =
TEXT("Software\\Microsoft\\Microsoft Games\\Debug\\EnableTraceLocks");
TCRegKeyHandle hkey;
LONG lResult =
RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszKeyName, 0, KEY_READ, &hkey);
if (ERROR_SUCCESS == lResult)
{
// Attempt to read the value with the specified class name
DWORD dwTraceLocks = 0;
DWORD dwType = REG_DWORD;
DWORD dwDataSize = sizeof(dwTraceLocks);
USES_CONVERSION;
lResult = RegQueryValueEx(hkey, A2CT(pszClassName), NULL, &dwType,
LPBYTE(&dwTraceLocks), &dwDataSize);
if (ERROR_SUCCESS == lResult && REG_DWORD == dwType)
bEnable = dwTraceLocks != 0;
}
// Add the class name and the enabled flag to the map
mapClassNames.insert(CClassNameMap::value_type(pszClassName, bEnable));
return bEnable;
}
#else // _ENABLE_TRACE_LOCKS
inline bool TCObjectLock_EnableTraceLocks(const char* pszClassName)
{
return false;
}
#endif // _ENABLE_TRACE_LOCKS
#endif // _DEBUG
/////////////////////////////////////////////////////////////////////////////
// TCObjectLock provides a convenient mechanism for the locking and unlocking
// of protected resources.
//
// The TCObjectLock template class can be used for any object, /T/, that has
// the public members, Lock and Unlock, each without a parameter list.
//
// This object is constructed with a pointer to the attached object, /T/, and
// the Lock method of that object is called. When this object goes out of
// scope, the Unlock method of the attached object is automatically called.
// Alternately, the Unlock method of this object will unlock the attached
// object prior to this object going out of scope.
//
// The following is an example usage of this class:
//
// DWORD CMyObject::GetValue() const
// {
// TCObjectLock<CMyObject> lock(this);
// return m_dwValue;
// } // Unlock is called automatically when lock goes out of scope
//
// Note that this example assumes that the CMyObject class provides the
// public member functions, Lock and Unlock.
//
// It is common for a lockable object to declare a typedef for TCObjectLock.
// For example, if the CMyObject declaration provides the following typedef:
//
// typedef TCObjectLock<CMyObject> XLock;
//
// then, the previous examples could be changed to the following, using the
// more abbreviated notation:
//
// DWORD CMyObject::GetValue() const
// {
// XLock lock(this);
// return m_dwValue;
// } // Unlock is called automatically when lock goes out of scope
//
// Parameters:
// T - Any type that publicly exposes both a Lock and an Unlock method,
// both of which do not take any parameters.
template <class T>
class TCObjectLock
{
// Construction / Destruction
public:
TCObjectLock(T* pObject);
~TCObjectLock();
// Operations
public:
void Unlock();
// Data Members:
protected:
T* m_pObject; // A pointer to the lockable class, /T/
};
/////////////////////////////////////////////////////////////////////////////
// Group=Construction / Destruction
/////////////////////////////////////////////////////////////////////////////
// Locks the object, /T/, by calling the Lock method of class /T/.
template <class T>
inline TCObjectLock<T>::TCObjectLock(T* pObject)
: m_pObject(pObject)
{
if (m_pObject)
m_pObject->Lock();
#ifdef _ENABLE_TRACE_LOCKS
#ifdef _DEBUG
const char* pszClass = TCTypeName(T);
if (TCObjectLock_EnableTraceLocks(pszClass))
{
DWORD dwID = GetCurrentThreadId();
_TRACE3("TCObjectLock<%hs> on thread 0x%0X (%d): Locking\n",
pszClass, dwID, dwID);
}
#endif // _DEBUG
#endif // _ENABLE_TRACE_LOCKS
}
/////////////////////////////////////////////////////////////////////////////
// Unlocks the object, /T/, if Unlock has not already been called.
template <class T>
inline TCObjectLock<T>::~TCObjectLock()
{
if (m_pObject)
Unlock();
}
/////////////////////////////////////////////////////////////////////////////
// Group=Operations
/////////////////////////////////////////////////////////////////////////////
// Unlocks the object, /T/, by calling the Unlock method of class /T/.
//
// After unlocking the object, the m_pObject data member is set to NULL so
// that the destructor does not attempt to unlock it.
//
// See Also: TCThread::destructor
template <class T>
inline void TCObjectLock<T>::Unlock()
{
#ifdef _ENABLE_TRACE_LOCKS
#ifdef _DEBUG
const char* pszClass = TCTypeName(T);
if (TCObjectLock_EnableTraceLocks(pszClass))
{
DWORD dwID = GetCurrentThreadId();
_TRACE3("TCObjectLock<%hs> on thread 0x%0X (%d): Unlocking\n",
pszClass, dwID, dwID);
}
#endif // _DEBUG
#endif // _ENABLE_TRACE_LOCKS
if (m_pObject)
{
m_pObject->Unlock();
m_pObject = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
#endif // __ObjectLock_h__