#include <archon/util/ref.H>
Collaboration diagram for Archon::Utilities::Ref< T >:
Public Types | |
typedef T | ObjectType |
Public Member Functions | |
Ref () | |
Ref (T *p) | |
A pointer to a _dynamically_ allocated instance of a class inherited from RefObjectBase. | |
Ref (const Ref &r) | |
template<typename U> | |
Ref (const Ref< U > &r) | |
~Ref () | |
void | reset (T *p=0) |
With no argument this method breaks the reference to the previously referenced object and makes this reference a null reference. | |
void | swap (Ref &r) |
Efficient swapping that avoids access to the referenced object, particularly its reference count. | |
Ref & | operator= (const Ref &) |
template<typename U> | |
Ref & | operator= (const Ref< U > &) |
template<typename U> | |
bool | operator== (const Ref< U > &) const |
template<typename U> | |
bool | operator!= (const Ref< U > &) const |
template<typename U> | |
bool | operator< (const Ref< U > &) const |
template<typename U> | |
bool | operator> (const Ref< U > &) const |
T * | operator-> () const |
operator bool () const | |
T * | get () const |
When using this method and 'operator->' in multi threaded environments it is important to make sure that the returned pointer remains valid. | |
bool | sole () const |
Is this the sole reference to some object? | |
Ref (T *p, RefNoLockTag) | |
Can be used to create a reference in a context where the object mutex is locked. | |
Ref (T *p, RefSafeIncTag) | |
Contructs a real reference if the argument is not null and if the use count of the referenced object is at least one, otherwise it constructs a null reference. |
The intrusive smart pointer has one very nice feature compared with non-intrusive alternatives, that is, it allows several smart pointer to be constructed from the same regular pointer without loosing track of the usage count. Also one can seamlessly convert from smart pointer to regular pointer and back again.
The big downside is of cause that it can only be used to point to objects that are derived from RefObjectBase which contains the usage count.
Ref's may be used to point to constant objects. Of course, since the reference count is a part of that object, we are talkning about constness in a logical sence. The reference count will always be a mutable member of the referenced objects.
Reference counted smart pointer are extreemly useful in multi threaded scenarios. That is, holding a smart pointer to some object is a guarantee that the object is not deallocated yet. This in turn guarantees that when some thread invokes a method via the smart pointer, it is safe to the extent that the target object vill not be deleted by some other thread while the method is being executed.
To be generally usefull it must be safe for multiple threads to concurrently create and destroy references to the same object. To accomodate this, any access to the usage count is secured by a critical section which is enforced by a mutex inside each RefObjectBase instance.
getSafeRef
It is possible with a pre-declaration of a class A to declare a member Ref in a class B without a definition of A in the compilation unit. It does however require that you override the implicit constructor and destructor and define them in an other compilation unit where A is defined. Also every method whos signature involves Ref must be used and defined in a compilation unit where A is defined.
Definition at line 167 of file ref.H.
|
A pointer to a _dynamically_ allocated instance of a class inherited from RefObjectBase. Note "dynamically allocated"! |
|
Can be used to create a reference in a context where the object mutex is locked. It should only be used in refDispose. |
|
Contructs a real reference if the argument is not null and if the use count of the referenced object is at least one, otherwise it constructs a null reference. This constructor has no relevans in sigle threaded applications since when the usage count drops to zero the object is destroyed imediately. In multi threaded environmets, the situation is different. Here one thread may access a reference counted object in the period of time between the point where the usage count drops to zero and the point where it is completely destroyed. Note that this normally implies that the accessing thread is using a conventional pointer to access the object. It also implies that another thread is discarding the last Ref reference to the object. You may say "Why even bother with this situation? The thread should be using the Ref references anyway, and then there would be no need for this method.". Well, that is almost true. Never the less, there are situations where this method is helpfull, and where it makes sence to store a reference in a convetional pointer. One such situation is when a thread (A) wishes to store a reference to a reference counted object in the intent to access it later. At the same time this thread does not want to postpone the destruction of the object anymore that it would have without a reference (the key issue). Now, this is where the conventional pointer come into play. The only way that a reference can be held without postponing the destruction of the object, is by using a conventional pointer instead of a Ref. The next problem is of course that the conventional pointer may become dangeling (pointing to a destoyed object) at any time because another thread (D) discards the last Ref reference to the object. As such, the conventional pointer will be of no use to tread A. The trick is to provide a way for the object destructor to nullify the conventional pointer held by thread A. Now, with appropriate use of mutexes thread A can at any time determine if the object is still alive. Determining if the object is still alive is seldom enough. Thread A wants to access the object. It wants to call methods on it. This demand raises new concerns. First of all, to be sure that the object stays alive during the access, we must protect it with the same mutex (M) that also protects the nullification performed by thread B. Now, lets say that the object is of a class derived from the class with the destructor that nullifies the poiner. This means that it is possible that thread A accesses a partially destroyed object. Therefor it is not safe to use virtual methods during the access by thread A. Another problem with accessing the object from within mutex M is that for the time the access takes, thread B will be blocked trying to discard the last reference. This is of cource not a problem if the access is fast. A third problem with accessing the object from within mutex M is that thread A must be carefull not to attempt to enter a mutex that is locked by thread B. This would cause a deadlock. The thing that should always be avoided is for two threads (A and B) to attempt to lock two mutexes in opposite order. If more than two mutexes are involved the general rule is that they must be aquired in the same order my all threads. If we could raise the reference count of the object just before the access and then lower it again afterwards, then it seems that we would be able to solve all three problems above. That is, the reference count will not drop to zero during the access. Therefore the object cannot become partially destroyed. Thread B will not be blocked since the reference it discards is not the last one. And finally thread A does not need to keep a lock on mutex M during the access, and the risk of deadlock will then be smaller. So, it should not be too difficult to raise the reference count of the object. Why not just construct a Ref from the pointer while holding a lock on mutex M? Well, this will not do, since if the reference has dropped to zero before the Ref construction then the object will be deleted anyway when thread A leave mutex M. Ouch! What we need is the 'Ref(T *, RefSafeIncTag)' sonstructor. This one attempts to construct a new Ref reference from a conventional pointer, but the news is that if the usage count already has dropped to zero then it creates a null reference. It does this atomically with respect to the standard increment and decrement opreations of the reference counted object. Ok, what must I do then? Do something like the following: struct A: Thread { Mutex m; R *r; void dissociate() { Mutex::Lock l(m); r = 0; } void main() { Mutex::Lock l(m); Ref<R> safeRef(r, RefSafeIncTag()); l.release(); if(safeRef) safeRef->access(); } }; struct R: RefObjectBase // Reference counted object { Ref a; ~O { a->dissociate(); } void access() { ... } }; Finally note that to adhere to our previously stated demand that thread A should not postpone the destruction of the object, we should add a Thread::terminate(self) to the 'dissociate' method above and make sure that whatever the 'access' method does it will be willing to meet this termination request. |
|
|
|
|
With no argument this method breaks the reference to the previously referenced object and makes this reference a null reference. Otherwise it breaks the reference to the previously referenced object and creates a new reference to the object pointed to by the argument. Definition at line 970 of file ref.H. Referenced by Archon::X3D::NodeCustomField< C >::forwardClear(), Archon::Display::ConnectionX11::getScreen(), Archon::Display::ScreenX11::getVisual(), Archon::Utilities::Stream::Connector< C >::main(), Consumer::main(), Producer::main(), Archon::Utilities::Ref< T >::operator=(), Archon::X3D::SubSceneNode::refForwardDestroy(), and Archon::Display::Bind::release(). |