Archon::Utilities::Ref< T > Struct Template Reference

Intrusive reference counted smart pointer. More...

#include <archon/util/ref.H>

Collaboration diagram for Archon::Utilities::Ref< T >:

Collaboration graph
List of all members.

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.
Refoperator= (const Ref &)
template<typename U>
Refoperator= (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
Toperator-> () const
 operator bool () const
Tget () 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.

Detailed Description

template<typename T>
struct Archon::Utilities::Ref< T >

Intrusive reference counted smart pointer.

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 only be used to point to dynamically allocated objects. Never to automatc objects on the stack.

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.

Re-entrance/thread safty:

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.

Although the implicit manipulations of the usage count is thread safe, access to the reference variable itself is not thread safe. That is, the reference itself must not be accessed by more that one thread at a time. It is the responsibility of the application to ensure this.
See also:


Do NOT create refrerence variables in a contructor to the object being constructed. The immediate reason is that if an exception is thrown within the constructor then the object will be deallocated regardless of reference count and at some point in time when your reference is destroyed it will also destroy the object. It depends on the situation which one comes first, but the second one will lead to disaster. Even if you could guarantee than no exception is throw from the constructor, there are still good reasons not to create refrerence variables in a contructor to the object being constructed. One is that if the reference gets destroyed during the execution of the destructor such that the reference count drops back to zero then the object will get deallocated even before the costructor returns to the caller. The truth is that we cannot gain controll over the destruction of an object until the costructor returns.

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.

If we had a sufficiently large datatype for the reference count which we knew was atomic in read (sig_atomic_t), we could make an optimization in that if you read the count and find it to be < 2 then you need not aquire the lock since no other thread holds a Ref to the same object and there is no one who could access the object cocurently.
Even better, we could define a type and simple decrement and increment ops on it, to abstract away the details of simple atomic ops.

We should seriously considder making the Ref(T *) constructor explicit, since it actually is a relativly heavy operation and it might even be dangerous - for example when ref count has dropped to zero.

Considder recommending a scheme where Ref<>s can be automatically converted to plain pointers and that one should always use plain pointer arguments to functions.
This would avoid a lot on access to the reference counter. Or maybe even better, we might go and defined a new RefArg type to be use solely for argument passing. This would prevent the automatic conversion to plain pointers which might be a little too dangerous. then, of cause the RefArg type should never be used for anything else than function arguments. The idea is that RefArg should be copyable without accessing the reference count, but scince it is always ever used as a function argument, there will at least be a temporary Ref object keeping the object alive. There is at leat one danger to all this, if the called function leads to the nullification of the original Ref object we have the trouble. This should be quite rare though, since there generally would be no point in passing the argument in the first place, if the function has another was of accessing the value. Then again, it might not be that rare.

Definition at line 167 of file ref.H.

Constructor & Destructor Documentation

template<typename T>
Archon::Utilities::Ref< T >::Ref T p  )  [inline]

A pointer to a _dynamically_ allocated instance of a class inherited from RefObjectBase.

Note "dynamically allocated"!

Definition at line 177 of file ref.H.

template<typename T>
Archon::Utilities::Ref< T >::Ref T p,

Can be used to create a reference in a context where the object mutex is locked.

It should only be used in refDispose.

Definition at line 237 of file ref.H.

template<typename T>
Archon::Utilities::Ref< T >::Ref T p,

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.

Definition at line 1024 of file ref.H.

Member Function Documentation

template<typename T>
T* Archon::Utilities::Ref< T >::get  )  const [inline]

When using this method and 'operator->' in multi threaded environments it is important to make sure that the returned pointer remains valid.

There are at least three ways to ensure this: Applying the method to a constant Ref, Securing all access to the Ref with a Mutex or applying the method to a Ref on the stack of the executing thread.

This is also the method you would use when you need to dynamic cast.

Definition at line 225 of file ref.H.

Referenced by Archon::X3D::Route::add(), Archon::X3D::NodeSequenceCustomField< C >::add(), Archon::X3D::SimpleSeqCustomField< T >::add(), Archon::X3D::SAI::ExternalRoute::add(), Archon::X3D::NodeBase::add(), Archon::X3D::NodeSequenceField< N, C >::add(), Archon::X3D::SimpleSequenceField< N, T >::add(), Archon::X3D::SAI::Session::addRoute(), Archon::X3D::Viewer::bindViewpoint(), Archon::X3D::Route::cascadeEvent(), Archon::X3D::SAI::ExternalRoute::cascadeEvent(), Archon::X3D::XML::Parser::characterDataHandler(), Archon::X3D::Proxy::NodeBase::create(), Archon::X3D::Proxy::Torus::create(), Archon::X3D::Proxy::Script::create(), Archon::X3D::Proxy::TouchSensor::create(), Archon::X3D::Proxy::CylinderSensor::create(), Archon::X3D::Proxy::SphereSensor::create(), Archon::X3D::Proxy::PlaneSensor::create(), Archon::X3D::Proxy::Viewpoint::create(), Archon::X3D::Proxy::Billboard::create(), Archon::X3D::Proxy::ScalarInterpolator::create(), Archon::X3D::Proxy::PositionInterpolator2D::create(), Archon::X3D::Proxy::PositionInterpolator::create(), Archon::X3D::Proxy::OrientationInterpolator::create(), Archon::X3D::Proxy::NormalInterpolator::create(), Archon::X3D::Proxy::CoordinateInterpolator2D::create(), Archon::X3D::Proxy::CoordinateInterpolator::create(), Archon::X3D::Proxy::ColorInterpolator::create(), Archon::X3D::Proxy::SpotLight::create(), Archon::X3D::Proxy::PointLight::create(), Archon::X3D::Proxy::DirectionalLight::create(), Archon::X3D::Proxy::Text::create(), Archon::X3D::Proxy::FontStyle::create(), Archon::X3D::Proxy::Shape::create(), Archon::X3D::Proxy::ElevationGrid::create(), Archon::X3D::Proxy::IndexedFaceSet::create(), Archon::X3D::Proxy::Sphere::create(), Archon::X3D::Proxy::PointSet::create(), Archon::X3D::Proxy::IndexedLineSet::create(), Archon::X3D::Proxy::Cylinder::create(), Archon::X3D::Proxy::Cone::create(), Archon::X3D::Proxy::Box::create(), Archon::X3D::Proxy::Background::create(), Archon::X3D::Proxy::Fog::create(), Archon::X3D::Proxy::Appearance::create(), Archon::X3D::Proxy::Material::create(), Archon::X3D::Proxy::PixelTexture::create(), Archon::X3D::Proxy::ImageTexture::create(), Archon::X3D::Proxy::TextureTransform::create(), Archon::X3D::Proxy::TextureCoordinate::create(), Archon::X3D::Proxy::Normal::create(), Archon::X3D::Proxy::Coordinate::create(), Archon::X3D::Proxy::ColorRGBA::create(), Archon::X3D::Proxy::Color::create(), Archon::X3D::Proxy::Transform::create(), Archon::X3D::Proxy::Switch::create(), Archon::X3D::Proxy::Group::create(), Archon::X3D::Proxy::ApplicationLink::create(), Archon::X3D::Proxy::Inline::create(), Archon::X3D::Proxy::TimeSensor::create(), Archon::X3D::Proxy::MetadataString::create(), Archon::X3D::Proxy::MetadataSet::create(), Archon::X3D::Proxy::MetadataInteger::create(), Archon::X3D::Proxy::MetadataFloat::create(), Archon::X3D::Proxy::MetadataDouble::create(), Archon::X3D::SAI::ExternalRoute::del(), Archon::X3D::SAI::Session::delRoute(), Archon::Raytracer::SceneLoader::doShape(), Archon::X3D::XML::Parser::endElement(), Archon::X3D::VRML::Parser::Context::extract(), Archon::X3D::FieldBase::fetch(), Archon::X3D::NodeCustomField< C >::get(), Archon::Utilities::RefMap< NodeBase >::get(), Archon::Raytracer::get_scene(), Archon::X3D::SAI::Session::getFieldAccessType(), Archon::X3D::SAI::Session::getFieldName(), Archon::X3D::SAI::Session::getFieldType(), Archon::X3D::SAI::Session::getFieldValue(), Archon::X3D::SAI::Session::getFieldValueAt(), Archon::X3D::Proxy::ExecutionContext::getNamedNode(), Archon::X3D::SAI::Session::getRootGroup(), Archon::X3D::Proxy::ExecutionContext::getRootGroup(), Archon::Display::ConnectionX11::getScreen(), Archon::Display::ScreenX11::getVisual(), Archon::X3D::NodeSequenceCustomField< C >::inject(), Archon::X3D::NodeBase::inject(), Archon::X3D::NodeSequenceField< N, C >::inject(), Archon::X3D::XML::Parser::injectChild(), Archon::X3D::SAI::Session::launchApplicationScene(), Archon::Utilities::MethodArgRunner< T, U >::main(), Archon::Utilities::MethodVoidRunner< T >::main(), Archon::Raytracer::main(), Archon::Utilities::BackRef< T >::operator!=(), Archon::Utilities::BackRef< T >::operator==(), Archon::X3D::XML::Parser::SequenceValueParser< T >::parse(), Archon::X3D::XML::Parser::SingleValueParser< T >::parse(), Archon::X3D::VRML::parse(), Archon::Utilities::LrParserBase::parse(), Archon::X3D::XML::Parser::parseStartElement(), Archon::X3D::VRML::Parser::Printer::print(), Archon::Utilities::Regex::Parser::Printer::print(), Archon::X3D::Route::refDispose(), Archon::X3D::SAI::ExternalRoute::refDispose(), Archon::X3D::SAI::Session::registerFieldInterest(), Archon::X3D::NodeSequenceCustomField< C >::remove(), Archon::X3D::SimpleSeqCustomField< T >::remove(), Archon::X3D::NodeBase::remove(), Archon::X3D::NodeSequenceField< N, C >::remove(), Archon::X3D::SimpleSequenceField< N, T >::remove(), Archon::Utilities::RefMap< NodeBase >::remove(), Archon::X3D::Text::render(), Archon::X3D::ElevationGrid::render(), Archon::X3D::PointSet::render(), Archon::X3D::IndexedLineSet::render(), Archon::X3D::Viewer::renderFrame(), Archon::X3D::Viewer::resetViewpoint(), Archon::X3D::NodeSequenceCustomField< C >::set(), Archon::X3D::NodeCustomField< C >::set(), Archon::X3D::SimpleSeqCustomField< T >::set(), Archon::X3D::SimpleCustomField< T >::set(), Archon::X3D::NodeSequenceField< N, C >::set(), Archon::X3D::NodeField< N, C >::set(), Archon::X3D::SimpleField< N, vector< T > >::set(), Archon::X3D::NodeSequenceCustomField< C >::setAt(), Archon::X3D::SimpleSeqCustomField< T >::setAt(), Archon::X3D::NodeSequenceField< N, C >::setAt(), Archon::X3D::SimpleSequenceField< N, T >::setAt(), Archon::X3D::SAI::Session::setFieldValue(), Archon::X3D::SAI::Session::setFieldValueAdd(), Archon::X3D::SAI::Session::setFieldValueAt(), Archon::X3D::SAI::Session::setFieldValueDel(), Archon::X3D::SAI::Session::setRootGroup(), Archon::X3D::Loader::submitJob(), Archon::X3D::VRML::Parser::Context::typeName(), Archon::X3D::SAI::Session::unRegisterFieldInterest(), Archon::X3D::Viewer::ShapeCache::updateTexture(), Archon::X3D::Viewer::Viewer(), and Archon::X3D::SAI::Session::withdrawApplicationScene().

template<typename T>
T * Archon::Utilities::Ref< T >::operator->  )  const [inline]

See also:

Definition at line 1017 of file ref.H.

template<typename T>
void Archon::Utilities::Ref< T >::reset T p = 0  )  [inline]

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().

The documentation for this struct was generated from the following file:
Generated on Sun Jul 30 22:57:50 2006 for Archon by  doxygen 1.4.4