mirror of https://github.com/lianthony/NT4.0
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.
422 lines
13 KiB
422 lines
13 KiB
// RefCnt.h -- Class definition for reference counted objects
|
|
|
|
#ifndef __REFCNT_H__
|
|
|
|
#define __REFCNT_H__
|
|
|
|
class CPersist;
|
|
class CRCObject;
|
|
|
|
#ifdef _DEBUG
|
|
|
|
// The WithType define is used in the debugging build to record the type name
|
|
// for a class object. It also records the file name and line number where the
|
|
// object was created.Use the WithType define in the header of each constructor
|
|
// for the object type you wish to tag.
|
|
|
|
// #define RECORD_TYPE(typename) CRCObject(typename) // , THIS_FILE, __LINE__)
|
|
#define WithType(TypeName) (TypeName)
|
|
|
|
// Beware! The #defines below are careful to mention pObject only once. We
|
|
// follow that discipline to allow pObject to be a creation expression
|
|
// (i.e., new CObjectType(...) ). If you change the defines, maintain
|
|
// that discipline.
|
|
//
|
|
// We also assume varpObject is a pointer variable with the same type
|
|
// as pObject.
|
|
|
|
// The AttachRef, ChangeRef, and DetachRef defines are used to keep track of the
|
|
// member variables which point to reference counted objects. In the retail
|
|
// environment they simply implement reference counting so that objects are
|
|
// automatically deleted when the reference counts go from one to zero. In the
|
|
// debug environment we actually keep lists of the member variables which refer
|
|
// to each object. To use these defines you must derive your class directly or
|
|
// indirectly from CRCObject, and you must invoke the DECLARE_REF_COUNTERS define
|
|
// in the public section of your class definition.
|
|
//
|
|
// You can also use these defines to keep track of references stored in local
|
|
// variables and global variables. However you should be avoid their use when
|
|
// you're returning a reference as the explicit result of a function. Consider
|
|
// the pattern:
|
|
//
|
|
// AttachRef(pMyObject, new CMyObject(...));
|
|
//
|
|
// ... Processing which changes *pMyObject ...
|
|
//
|
|
// DetachRef(pMyObject); // Should use ForgetRef(pMyObject) instead...
|
|
//
|
|
// return pMyObject;
|
|
//
|
|
// This code will fail because the call to DetachRef will deallocate the object
|
|
// and set pMyObject to NULL. In cases like this use the ForgetRef define instead.
|
|
// It will decrement the reference count without setting the pointer variable to
|
|
// NULL, and it won't delete the object if it's ref count goes to zero. This is
|
|
// the only case where you should use ForgetRef.
|
|
|
|
// Use these defines to adjust the reference counts
|
|
// for member variables in the current class.
|
|
|
|
// You can also use the defines to reference count
|
|
// local stack frame variables.
|
|
|
|
#define AttachRefAndOwner(varpObject, pObject, pOwner) (( pObject)->AttachReference(&(varpObject), pOwner))
|
|
#define ChangeRefAndOwner(varpObject, pObject, pOwner) (( pObject)->ChangeReference(&(varpObject), pOwner))
|
|
#define DetachRefAndOwner(varpObject, pOwner) ((varpObject)->DetachReference(&(varpObject), TRUE , pOwner))
|
|
#define ForgetRefAndOwner(varpObject, pOwner) ((varpObject)->DetachReference(&(varpObject), FALSE, pOwner))
|
|
|
|
#define AttachRef(varpObject, pObject) AttachRefAndOwner(varpObject, pObject, this)
|
|
#define ChangeRef(varpObject, pObject) ChangeRefAndOwner(varpObject, pObject, this)
|
|
#define DetachRef(varpObject) DetachRefAndOwner(varpObject, this)
|
|
#define ForgetRef(varpObject) ForgetRefAndOwner(varpObject, this)
|
|
|
|
// Use the ClsXXX defines when you want to adjust the
|
|
// reference count for member variable in another class.
|
|
|
|
#define ClsAttachRef(pClass, varpObject, pObject) (( pObject)->AttachReference(&(pClass->varpObject), pClass))
|
|
#define ClsChangeRef(pClass, varpObject, pObject) (( pObject)->ChangeReference(&(pClass->varpObject), pClass))
|
|
#define ClsDetachRef(pClass, varpObject) ((pClass->varpObject)->DetachReference(&(pClass->varpObject), TRUE , pClass))
|
|
#define ClsForgetRef(pClass, varpObject) ((pClass->varpObject)->DetachReference(&(pClass->varpObject), FALSE, pClass))
|
|
|
|
// Use the PAttachRef, PChangeRef, PDetachRef, and PForgetRef when you have a pointer
|
|
// to pointer variable instead of the variable itself.
|
|
|
|
#define PAttachRef(ppObject, pObject) (( pObject)->AttachReference((ppObject), this))
|
|
#define PChangeRef(ppObject, pObject) (( pObject)->ChangeReference((ppObject), this))
|
|
#define PDetachRef(ppObject) ((*ppObject)->DetachReference((ppObject), TRUE , this))
|
|
#define PForgetRef(ppObject) ((*ppObject)->DetachReference((ppObject), FALSE, this))
|
|
|
|
#define ATTACH_PARMS CRCObject **ppObject, PVOID pvClass
|
|
#define DETACH_PARMS CRCObject **ppObject, BOOL fAutoDelete, PVOID pvClass
|
|
|
|
// The DECLARE_REF_COUNTERS define will define a set of reference counting functions
|
|
// for your class. Their function is to force type validation on the pointer variable
|
|
// and the address expression.
|
|
|
|
#define DECLARE_REF_COUNTERS(class_type) inline void AttachReference(class_type **ppObject, PVOID pvClass) \
|
|
{ CRCObject::RawAttachReference((CRCObject **) ppObject, pvClass); } \
|
|
inline void ChangeReference(class_type **ppObject, PVOID pvClass) \
|
|
{ CRCObject::RawChangeReference((CRCObject **) ppObject, pvClass); } \
|
|
inline void DetachReference(class_type **ppObject, BOOL fAutoDelete, PVOID pvClass) \
|
|
{ CRCObject::RawDetachReference((CRCObject **) ppObject, fAutoDelete, pvClass); }
|
|
|
|
#else
|
|
|
|
#define WithType(TypeName) ()
|
|
|
|
#define AttachRefAndOwner(varpObject, pObject, pOwner) (( pObject)->AttachReference(&(varpObject) ))
|
|
#define ChangeRefAndOwner(varpObject, pObject, pOwner) (( pObject)->ChangeReference(&(varpObject) ))
|
|
#define DetachRefAndOwner(varpObject, pOwner) ((varpObject)->DetachReference(&(varpObject), TRUE ))
|
|
#define ForgetRefAndOwner(varpObject, pOwner) ((varpObject)->DetachReference(&(varpObject), FALSE))
|
|
|
|
#define AttachRef(varpObject, pObject) (pObject)->AttachReference(&(varpObject))
|
|
#define ChangeRef(varpObject, pObject) (pObject)->ChangeReference(&(varpObject))
|
|
#define DetachRef(varpObject) ((varpObject)->DetachReference(&(varpObject), TRUE ))
|
|
#define ForgetRef(varpObject) ((varpObject)->DetachReference(&(varpObject), FALSE))
|
|
|
|
#define ClsAttachRef(pClass, varpObject, pObject) (( pObject)->AttachReference(&(pClass->varpObject) ))
|
|
#define ClsChangeRef(pClass, varpObject, pObject) (( pObject)->ChangeReference(&(pClass->varpObject) ))
|
|
#define ClsDetachRef(pClass, varpObject) ((pClass->varpObject)->DetachReference(&(pClass->varpObject), TRUE ))
|
|
#define ClsForgetRef(pClass, varpObject) ((pClass->varpObject)->DetachReference(&(pClass->varpObject), FALSE))
|
|
|
|
#define PAttachRef(ppObject, pObject) (( pObject)->AttachReference((ppObject)))
|
|
#define PChangeRef(ppObject, pObject) (( pObject)->ChangeReference((ppObject)))
|
|
#define PDetachRef(ppObject) ((*ppObject)->DetachReference((ppObject), TRUE ))
|
|
#define PForgetRef(ppObject) ((*ppObject)->DetachReference((ppObject), FALSE))
|
|
|
|
#define ATTACH_PARMS CRCObject **ppObject
|
|
#define DETACH_PARMS CRCObject **ppObject, BOOL fAutoDelete
|
|
|
|
#define DECLARE_REF_COUNTERS(class_type) inline void AttachReference(class_type **ppObject) \
|
|
{ CRCObject::RawAttachReference((CRCObject **) ppObject); } \
|
|
inline void ChangeReference(class_type **ppObject) \
|
|
{ CRCObject::RawChangeReference((CRCObject **) ppObject); } \
|
|
inline void DetachReference(class_type **ppObject, BOOL fAutoDelete) \
|
|
{ CRCObject::RawDetachReference((CRCObject **) ppObject, fAutoDelete); }
|
|
|
|
#endif
|
|
|
|
typedef struct _OwnerLink
|
|
{
|
|
struct _OwnerLink *polNext;
|
|
PVOID pvClass;
|
|
CRCObject **pprcObj;
|
|
CRCObject *prcObj;
|
|
|
|
} OwnerLink;
|
|
|
|
typedef OwnerLink *POwnerLink;
|
|
|
|
#ifdef _DEBUG
|
|
|
|
class CObjectAccountant;
|
|
|
|
class CObjectCounter
|
|
{
|
|
friend class CRCObject;
|
|
friend class CObjectAccountant;
|
|
|
|
public:
|
|
|
|
// Constructor --
|
|
|
|
CObjectCounter();
|
|
|
|
// Destructor --
|
|
|
|
~CObjectCounter();
|
|
|
|
private:
|
|
|
|
// Counting routines --
|
|
|
|
void ObjectBirth (CRCObject *prcObj);
|
|
void ObjectDeath (CRCObject *prcObj);
|
|
void AddReference(CRCObject *prcObj);
|
|
void SubReference(CRCObject *prcObj);
|
|
|
|
// Existence Tests --
|
|
|
|
BOOL ObjectRecorded(CRCObject *prcObj);
|
|
|
|
CRCObject *m_prcObjectFirst;
|
|
UINT m_crcObject;
|
|
UINT m_crcObjRef;
|
|
};
|
|
|
|
extern CObjectCounter ObjectCounter;
|
|
|
|
class CObjectAccountant
|
|
{
|
|
public:
|
|
|
|
// Constructor --
|
|
|
|
CObjectAccountant(int cObjDelta, int cRefDelta);
|
|
|
|
// Destructor --
|
|
|
|
~CObjectAccountant();
|
|
|
|
private:
|
|
|
|
int m_cObjStarting;
|
|
int m_cRefStarting;
|
|
int m_cObjDelta;
|
|
int m_cRefDelta;
|
|
};
|
|
|
|
#define PredictObjects(Name, cObjDelta, cRefDelta) CObjectAccountant Name(cObjDelta, cRefDelta)
|
|
|
|
#define ReportBirth() ObjectCounter.ObjectBirth(this)
|
|
#define ReportDeath() ObjectCounter.ObjectDeath(this)
|
|
#define AddRef() ObjectCounter.AddReference(this)
|
|
#define SubRef() ObjectCounter.SubReference(this)
|
|
|
|
#else // _DEBUG
|
|
|
|
#define PredictObjects(Name, cObjDelta, cRefDelta)
|
|
|
|
#define ReportBirth()
|
|
#define ReportDeath()
|
|
#define AddRef()
|
|
#define SubRef()
|
|
|
|
#endif // _DEBUG
|
|
|
|
class CRCObject
|
|
{
|
|
#ifdef _DEBUG
|
|
friend class CObjectCounter;
|
|
#endif // _DEBUG
|
|
|
|
public:
|
|
|
|
// Destructor --
|
|
|
|
virtual ~CRCObject();
|
|
|
|
#ifdef _DEBUG
|
|
virtual void StoreImage(CPersist *pDiskImage);
|
|
#else
|
|
virtual void StoreImage(CPersist *pDiskImage) {};
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
static void SkipImage(CPersist *pDiskImage);
|
|
#else
|
|
static void SkipImage(CPersist *pDiskImage) {};
|
|
#endif
|
|
|
|
protected:
|
|
|
|
// Constructor --
|
|
|
|
#ifdef _DEBUG
|
|
CRCObject(PSZ pszTypeName); // , PSZ pszFile, UINT iLine);
|
|
#else
|
|
CRCObject();
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
virtual void ConnectImage(CPersist *pDiskImage);
|
|
#else
|
|
virtual void ConnectImage(CPersist *pDiskImage) {};
|
|
#endif
|
|
|
|
|
|
// Reference Count Routines --
|
|
|
|
void RawAttachReference(ATTACH_PARMS);
|
|
void RawDetachReference(DETACH_PARMS);
|
|
void RawChangeReference(ATTACH_PARMS);
|
|
|
|
private:
|
|
|
|
int m_cReferences;
|
|
|
|
#ifdef _DEBUG
|
|
|
|
PSZ m_pszTypeName;
|
|
// PSZ m_pszFile;
|
|
// UINT m_iFile;
|
|
POwnerLink m_pol;
|
|
CRCObject *m_prcObjNext;
|
|
CRCObject **m_pprcObjLast;
|
|
|
|
#endif // _DEBUG
|
|
|
|
};
|
|
|
|
typedef CRCObject *PCRCObject;
|
|
|
|
#ifdef _DEBUG
|
|
inline CRCObject::CRCObject(PSZ pszTypeName) // , PSZ pszFile, UINT iLine)
|
|
#else // _DEBUG
|
|
inline CRCObject::CRCObject()
|
|
#endif
|
|
{
|
|
m_cReferences= 0;
|
|
|
|
#ifdef _DEBUG
|
|
|
|
ASSERT(pszTypeName);
|
|
|
|
m_pszTypeName = pszTypeName;
|
|
// m_pszFile = pszFile;
|
|
// m_iLine = iLine;
|
|
m_pol = NULL;
|
|
m_prcObjNext = NULL;
|
|
m_pprcObjLast = NULL;
|
|
|
|
ReportBirth();
|
|
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
inline CRCObject::~CRCObject()
|
|
{
|
|
ASSERT(m_cReferences == 0 );
|
|
ASSERT(m_pol == NULL);
|
|
|
|
ReportDeath();
|
|
}
|
|
|
|
inline void CRCObject::RawAttachReference(ATTACH_PARMS)
|
|
{
|
|
ASSERT(ppObject && pvClass);
|
|
ASSERT(*ppObject == NULL);
|
|
|
|
#ifdef _DEBUG
|
|
|
|
POwnerLink pol;
|
|
|
|
for (pol= m_pol; pol; pol= pol->polNext)
|
|
{
|
|
ASSERT(pol->prcObj == this);
|
|
ASSERT(pol->pprcObj != ppObject || pol->pvClass != pvClass);
|
|
}
|
|
|
|
// Note: We don't use __try/__finally code around the
|
|
// polNew allocation below because it only exists
|
|
// in Debug code.
|
|
|
|
POwnerLink polNew= New OwnerLink;
|
|
|
|
polNew->polNext= m_pol;
|
|
polNew->pprcObj= ppObject;
|
|
polNew->pvClass= pvClass;
|
|
polNew->prcObj = this;
|
|
|
|
m_pol= polNew;
|
|
|
|
#endif // _DEBUG
|
|
|
|
*ppObject= this;
|
|
|
|
++m_cReferences;
|
|
|
|
AddRef();
|
|
}
|
|
|
|
inline void CRCObject::RawDetachReference(DETACH_PARMS)
|
|
{
|
|
ASSERT(ppObject && pvClass);
|
|
ASSERT(*ppObject != NULL);
|
|
|
|
#ifdef _DEBUG
|
|
|
|
ASSERT(m_cReferences > 0);
|
|
ASSERT(m_pol != NULL);
|
|
|
|
BOOL fFound;
|
|
POwnerLink pol, *ppolLast;
|
|
|
|
for (fFound= FALSE, ppolLast= &m_pol, pol= *ppolLast;
|
|
pol;
|
|
ppolLast= &(pol->polNext), pol= *ppolLast
|
|
)
|
|
{
|
|
ASSERT(pol->prcObj == this);
|
|
|
|
if (pol->pprcObj == ppObject && pol->pvClass == pvClass)
|
|
{
|
|
*ppolLast= pol->polNext;
|
|
|
|
delete pol;
|
|
|
|
fFound= TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(fFound);
|
|
|
|
#endif // _DEBUG
|
|
|
|
if (fAutoDelete) *ppObject= NULL;
|
|
|
|
SubRef();
|
|
|
|
if (!--m_cReferences && fAutoDelete) delete this;
|
|
}
|
|
|
|
inline void CRCObject::RawChangeReference(ATTACH_PARMS)
|
|
{
|
|
ASSERT(ppObject && pvClass);
|
|
|
|
if (*ppObject)
|
|
#ifdef _DEBUG
|
|
(*ppObject)->RawDetachReference(ppObject, TRUE, pvClass);
|
|
#else // _DEBUG
|
|
(*ppObject)->RawDetachReference(ppObject, TRUE);
|
|
#endif // _DEBUG
|
|
|
|
#ifdef _DEBUG
|
|
RawAttachReference(ppObject, pvClass);
|
|
#else // _DEBUG
|
|
RawAttachReference(ppObject);
|
|
#endif
|
|
}
|
|
|
|
#endif // __REFCNT_H__
|