Click to See Complete Forum and Search --> : Reference counting problem


ProgramArtist
April 9th, 2008, 08:34 AM
Hi,

Maybe this is a c++ question but I guess it is general (although I encountered it using MS VC++ 6.0).

I created a class (and many classes derived from that) with reference counting and auto-freeing (just like a garbage collection).
If I assign a pointer to a CFooReferenceCounting object I call IncRefCount() and assigning any other value (incl. NULL) I'll always call DecRefCount(). This works fine since I overloaded all used operators in the FooReferenceCounting-holding class (it's my own TVARIANT class for scripting purposes).

I added the code (actually some snippets...) that you might understand what I have done.

I encountered the following problem:

If I declare a class CFoo_Very_Complex as a derivate of CFooReferenceCounting with an attribut of type
TVARIANT or TVARIANT* it is possible that I assign the value of this to this attribut.
Now the auto-freeing mechanism is broken since that CFooReferenceCounting will never be freed since there is always
another (sic!) element (actually the element itself!) pointing to it.

Has anyone any idea of a clean and safe solution for this?

Of course I can (e.g. in the app's idle) check for such circle-pointings ... but: Is this nice?
(Please note: If this could be the solution it has to be done recursively since I can have an object A
pointing to B pointing to C pointing to A again...

class CFooReferenceCounting:

// using MS VC 6.0
#include <afxtempl.h> // for the CList class template
class CFooReferenceCounting
{
public:
CFooReferenceCounting(){m_bAutoDelete = TRUE;m_iRefCount=0;};
virtual ~CFooReferenceCounting();
int GetRefC(){return m_iRefCount;};
virtual void IncRefC(TVARIANT* pTV);
virtual void DecRefC(TVARIANT* pTV);
protected:
BOOL m_bAutoDelete;
int m_iRefCount;
CList<tagTVARIANT*,tagTVARIANT*> m_refs;
}

CFooReferenceCounting::~CFooReferenceCounting()
{
if (GetRefC())
{
// set all references to PTR/NULL
m_bAutoDelete = FALSE; // to avoid suicide inside this function
POSITION pos = m_refs.GetHeadPosition();
while (pos)
{
tagTVARIANT* pTV = m_refs.GetNext(pos);
pTV->SetPtr(NULL); // includes a call to DecRefC of ourself
}
}
ASSERT(GetRefC()==0);
}

void CFooReferenceCounting::IncRefC(TVARIANT* pTV)
{
m_iRefCount++;
m_refs.AddTail(pTV);
}

void CFooReferenceCounting::DecRefC(TVARIANT* pTV)
{
m_iRefCount--;
POSITION pos = m_refs.Find(pTV);
ASSERT(pos);
if (pos)
{
m_refs.RemoveAt(pos);
}
if (m_iRefCount==0)
{
if (m_bAutoDelete)
{
delete [] this; // suicide !
}
}
}


The TVARIANT struct/class:

class CFooReferenceCounting; // forwarded here ...
typedef struct tagTVARIANT
{
int tv_iType;
int tv_iVal;
CString tv_sVal;
CFooReferenceCounting::* tv_pVal;
...
tagTVARIANT& operator=(tagTVARIANT& tvSource);
...
other operands are overwritten the same way of course ...
...
} TVARIANT;

tagTVARIANT& tagTVARIANT::operator=(tagTVARIANT& tvSource)
{
if (tv_iType==TVTYPE_PTR) tvSource.tv_pVal->DecRefC(this);
this->tv_iType = tvSource.tv_iType;
this->tv_iVal = tvSource.tv_iVal;
this->tv_fVal = tvSource.tv_fVal;
this->tv_pVal = tvSource.tv_pVal;
this->tv_sVal = tvSource.tv_sVal;
if (tv_iType==TVTYPE_PTR) tvSource.tv_pVal->IncRefC(this);

return *this;
}
...


With regards
Programartist
Ingo Bochmann

pm_kirkham
April 9th, 2008, 03:45 PM
1/ Why does 'this' need a reference to 'this'?

2/ Why do you need a list of references to 'this'

3/ If the number of self-references is known and fixed, decrement the ref count by that number.

ProgramArtist
April 10th, 2008, 04:03 AM
Thank you very much for answering,

let me answer to your questions:

First, generally: I use these data structures while implementing my own automation script language (similar to VBA, Phyton and so on). This works great; I really enjoyed writing my own parser and virtual machine although it took me some time to do so.

1/ Why does 'this' need a reference to 'this'?


Generally saying it does not make any sense. But I cannot circumvent it, since my back end user can define his own types with own structures and therefore he can let point an object to itself (e.g. by defining a "parent" pointer). Additionally I cannot circumvent that this user won't re-set the "parent" pointer at the moment he won't use the element itself anymore.

The code written by the user will look like the following example to create the situation described in my first post:

rem My Own Script Code Language
dim my_struct = tools.CreateStruct(); ! creates a templateless struct
if (!my_struct) exit();
my_struct.AddAtribut("v1"); ! adds an attribut "v1" of type Variant - the only type available
my_struct.v1 = "Testvalue"; ! works really great
my_struct.v1 = GetApplication(); ! example for setting the Variant to a "pointer" -> application->IncrefC(@my_struct) will be called.
my_struct.v1 = "another_value"; ! Now application->DecrefC(@my_struct) will be called

! Here comes the dangerous case:
my_struct.AddAttribut("selfptr");
my_struct.selfptr = my_struct;

my_struct = NULL; ! Here my_struct won't be destroyed from the heap
since it points to itself now. The same will occur if I set my_struct.v1 to an element which is using my_struct (e.g. with a "parent" ptr).


2/ Why do you need a list of references to 'this'

For the implementation of the VARIANT structure. The CFooReferenceCount objects will be destroyed automatically (if m_bAutoDelete is TRUE) when the last reference to them will be set to other values.
Actually I believe that you're right: If I implement all this stuff carefully maybe I do not need such a list. But for debugging purposes it was great...

3/ If the number of self-references is known and fixed, decrement the ref count by that number.

How do I know that number? I wrote that I can "calculate" it -> by recursively searching all objects..., but do I know?

With regards
Programartist
Ingo Bochmann

pm_kirkham
April 10th, 2008, 06:08 AM
How do I know that number? I wrote that I can "calculate" it -> by recursively searching all objects..., but do I know?
If you need to do that (which essentially is half way to being a cycle collector), then it isn't known and fixed. If it were, then you could arrange the constructor to always return an object with zero references. In your automation environment, you may have a factory for objects where you could make a similar adjustment.

Another thing that may work in your case, if the Attrib has a reference to the object which owns it, then setting its value to that object can cause it not to addref.

But in general for self-referential data structures, you either need a cycle collector or some other form of external reclamation.