Collaboration diagram for Archon::Utilities::BackRef< T >:
Public Member Functions
|BackRef (const Ref< T > &r)|
|BackRef (const BackRef &r)|
|BackRef (const BackRef< U > &r)|
|Break the reference to the previously referenced object and makes this reference a null reference. |
|void||reset (const Ref< T > &r)|
|Break the reference to the previously referenced object and creates a new reference to the specified object. |
|void||swap (BackRef &r)|
|Efficient swapping that avoids access to the referenced object particularly its reference count. |
|BackRef &||operator= (const BackRef &)|
|BackRef &||operator= (const BackRef< U > &)|
|bool||operator== (const BackRef< U > &) const|
|bool||operator!= (const BackRef< U > &) const|
|bool||operator== (const Ref< U > &) const|
|bool||operator!= (const Ref< U > &) const|
|bool||operator== (const U *) const|
|bool||operator!= (const U *) const|
|Ref< T >||operator-> () const throw (ForwardDestroyedException)|
|operator bool () const|
|Ref< T >||getRef () const throw (ForwardDestroyedException)|
The default meaning of this is that the referenced object gets deleted when both kinds of references drops to zero. The primary motivation for the BackRef is to allow two objects to refer to each other (A refers to B and B refers BACK to A). Without the special BackRef this would violate the acyclic clause of the Ref, and lead directly to memory leak. Note however that, even with the support of BackRef, working with cyclic references is a "black art" with very many pitfalls for the uninitiated, especially when the objects are accessed by multiple threads.
To undestand the general idea of BackRef, assume that we have an object A with a normal reference to an object B. Also, a number of outside parties hold a normal reference to A. In this case we expect A and consequently B to be deleted when the last outside party drops its reference. Now, if we add to B a normal reference to A, then we are in trouble, because A and B will keep each other alive forever. So how do we "repair" this situation. The trick is to distinguish between the references to A held by the outside parties and the one held by B, thus enabling us to detect when either one of them drops to zero, and not just when both of them does. The question is then, what shall we do when this happens, and that depends on the situation, but normally we should immitate the job carried out during object destruction. More specifically we should release all forward references held locally. In the example at hand, we could think of the references held by the outside parties and the one held by A as "forward" references, and of the reference to A held by B as a backward reference. Then when the forward reference count drops to zero in A we could drop its reference to B effectively leading to the deletion of both B and A.
This idea of distinguishing between forward and backward references scales nicely to larger hierachical object structures and can be applied recursivly. The first challenge you face is thus to mentally arrange your object structure in such a way that the direction become obvious, and such that the forward references and backward references satisfies the acyclic clause individually.
If you can guarantee that the forward references and backward references within a specific collection of objects satisfy the acyclic clause individually, and that when the forward reference count for an arbitrary object A in the collection drops to zero then every forward reference held by that object to other object in the collection will be released and that after this point then no new forward reference can be held by object A to an object in the collection, then you also have a guarantee that every object in the collection is deleted when the last reference from an outside party is released.
How do we know that during the execution of refForwardDestroy no other thread could access the object concurrently?
First we must assume that:
(next one is not really needed here)
Whenever a thread modifies or destroys a Ref variable that previously refered to some object it implicitly evaluates the following piece of pseudo code on behalf of that object:
Lock mutex If forwardCount == 1 Enter forwardDestroyed mode Unlock mutex Call refForwardDestroy Lock mutex Decrement forward count Unlock mutex
At the point in time thread 'A' put the object in forwardDestroyed mode because if found the forward count to be 1. At this exact point in time there can be no Ref variable pointing to the object except the one being accessed by thread 'A'.
Since all access to the object must occur through a Ref variable and since thread 'A' will be occupying the last one of them during its own execution of refForwardDestroy, the only way a second thread could access the object concurrently will if a new Ref variable can be created.
The only legal way that a new Ref variable can be created witout accessing an existing one, is through the Ref(T *, RefSafeIncTag) constructor or through the BackRef::getRef method. However, both methods fail when forwardDestroyed mode is entered.
This concludes the proof of the claim that during the execution of refForwardDestroy no other thread can access the object concurrently.
Definition at line 625 of file ref.H.