views:

822

answers:

6

When using reference counting, what are possible solutions/techniques to deal with circular references?

The most well-known solution is using weak references, however many articels about the subject imply that there are other methods as well, but keep repeating the weak-referencing example. Which makes me wonder, what are these other methods?

  • I am not asking what are alternatives to reference counting, rather what are solutions to circular references when using reference counting.

  • This question isn't about any specific problem/implementation/language rather a general question.

+5  A: 

I've looked at the problem a dozen different ways over the years, and the only solution I've found that works every time is to re-architect my solution to not use a circular reference.

Edit:

Can you expand? For example, how would you deal with a parent-child relation when the child needs to know about/access the parent?OB OB

As I said, the only good solution is to avoid such constructs unless you are using a runtime that can deal with them safely.

That said, if you must have a tree / parent-child data structure where the child knows about the parent, you're going to have to implement your own, manually called teardown sequence (i.e. external to any destructors you might implement) that starts at the root (or at the branch you want to prune) and does a depth-first search of the tree to remove references from the leaves.

It gets complex and cumbersome, so IMO the only solution is to avoid it entirely.

Randolpho
+1 same here. For a sample approach see my answer on another question: http://stackoverflow.com/questions/1047877/how-i-do-for-work-with-objects-with-composition/1047956#1047956
Fredrik Mörk
Can you expand? For example, how would you deal with a parent-child relation when the child needs to know about/access the parent?
OB OB
@ChrisW: Weak references were mentioned in the original question. OB OB was looking for alternatives.
Randolpho
A: 

There are couple of ways I know of for walking around this:

The first (and preferred one) is simply extracting the common code into third assembly, and make both references use that one

The second one is adding the reference as "File reference" (dll) instead of "Project reference"

Hope this helps

Nissim
The question is not about references between assmeblies, but references between objects (from the tags, in a reference-counting, ie non-garbage-collecting, environment)
AakashM
+2  A: 

Here is a solution I've seen:

Add a method to each object to tell it to release its references to the other objects, say call it Teardown().

Then you have to know who 'owns' each object, and the owner of an object must call Teardown() on it when they're done with it.

If there is a circular reference, say A <-> B, and C owns A, then when C's Teardown() is called, it calls A's Teardown, which calls Teardown on B, B then releases its reference to A, A then releases its reference to B (destroying B), and then C releases its reference to A (destroying A).

Alex Black
I think that in most cases, you'd like to call teardown from within the object's destructor. Calling teardown explicitly is somewhat similiar to destroying the object manually. (How do you know that nobody else is using the object when tearing it down?)
OB OB
Yes, you're right, call Teardown in the object's destructor. However, calling Teardown doesn't necessarily destroy the object, if someone else has a reference to it then it will stay alive. Objects only get destroyed when reference counts reach 0.
Alex Black
Let me correct that: Calling Teardown on an object does not necessarily destroy it, but it means the object is not longer useful, its similar to calling Dispose(). Since the object has released its references to the objects it depends on, its not safe to use.
Alex Black
@Alex Black: What if there are 2 objects C and D who own A. Then when C calls Teardown(), but D still wants it alive ? You have to have a counter in object A that will ignore all Teardown calls except last one.
alpav
@alpav: What we did was avoided that scenario. Only one of C or D could own A. Say C owns A and D does not, but D has a reference to A, then you have to design your code in such a way that D doesn't access A after C is gone, one way to do that is make D owned by C.
Alex Black
+1  A: 

I guess another method, used by garbage collectors, is "mark and sweep":

  1. Set a flag in every object instance
  2. Traverse the graph of every instance that's reachable, clearing that flag
  3. Every remaining instance which still has the flag set is unreachable, even if some of those instances have circular references to each other.
ChrisW
As i said, I am not asking for alternatives to refernce counting, but for solutions to circular references.
OB OB
What's the "problem" with circular references that you're trying to solve: is it the problem of garbage collection, or a different problem? If the problem is garbage collection, isn't an alternative the same thing as a solution?
ChrisW
@ChrisW: Yes, the problem is generally garbage collection. There are many methods for garbage collections. One of them is reference counting, another one is mark-and-sweep. Mark-and-sweep is an example of an alternative to reference counting as a gc method, weak-references are an example to a solution to circular references while still using reference counting as the gc method, on the other hand. I hope you see the difference.
OB OB
If you want to do it using only first-class references, then you need a way to detect circularity. Alex Black's method should do that: pretend to deference everything that a referenced object is pointing to, and if that experiment results in the original object's being dereferenced then it's a circular reference. I don't know but maybe that's an expensive way to do it: more expensive than mark and sweep, especially in pathological / long-chain cases.
ChrisW
You could actually use a garbage collector in tandem with reference counting. The reference count causes stuff to be cleaned up immediately if possible, and the GC handles the circular references (and if MOST things are automatically deleted via the reference count, the GC has less work to do!)
Grant Peters
+1  A: 

For solution to circular references, see here. :-)

Chris W. Rea
+1  A: 

I'd like to suggest a slightly different method that occured to me, I don't know if it has any official name:

Objects by themeselves don't have a reference counter. Instead, groups of one or more objects have a single reference counter for the entire group, which defines the lifetime of all the objects in the group.

In a similiar fashion, references share groups with objects, or belong to a null group.

A reference to an object affects the reference count of the (object's) group only if it's (the reference) external to the group.

If two objects form a circular reference, they should be made a part of the same group. If two groups create a circular reference, they should be united into a single group.

Bigger groups allow more reference-freedom, but objects of the group have more potential of staying alive while not needed.

OB OB
That's equivalent to stating which of your references are 'weak': if a reference is to an object in the same group then the reference is 'weak', else it's to an object in a different group and so that reference is 'strong'.
ChrisW
Yeah, only that if you have an external reference to object A that holds the weak reference to object B, and B is not otherwise referenced, B would still stay alive (which is the wanted behavior). You don't get that with "ordinary" weak references.
OB OB
Sounds similar to having discreet memory pools for different parts of the application (so you can just delete an entire pool at once and then its just cleaning up pointers into the pool), which is commonly used in games, and adding a reference count (which i guess would solve the problem of cleaning up pointers)
Grant Peters