diff --git a/include/osg/Observer b/include/osg/Observer index 07f98cbdb..57b6d5256 100644 --- a/include/osg/Observer +++ b/include/osg/Observer @@ -15,7 +15,7 @@ #define OSG_OBSERVER 1 #include -#include +#include #include namespace osg { @@ -35,12 +35,20 @@ class OSG_EXPORT Observer }; /** Class used by osg::Referenced to track the observers assoicated with it.*/ -class OSG_EXPORT ObserverSet +class OSG_EXPORT ObserverSet : public osg::Referenced { public: - ObserverSet(); - ~ObserverSet(); + ObserverSet(const Referenced* observedObject); + + Referenced* getObserverdObject() { return _observedObject; } + const Referenced* getObserverdObject() const { return _observedObject; } + + /** "Lock" a Referenced object i.e., protect it from being deleted + * by incrementing its reference count. + * + * returns null if object doesn't exist anymore. */ + Referenced* addRefLock(); inline OpenThreads::Mutex* getObserverSetMutex() const { return &_mutex; } @@ -57,8 +65,10 @@ class OSG_EXPORT ObserverSet ObserverSet(const ObserverSet& rhs) {} ObserverSet& operator = (const ObserverSet& rhs) { return *this; } + virtual ~ObserverSet(); mutable OpenThreads::Mutex _mutex; + Referenced* _observedObject; Observers _observers; }; diff --git a/include/osg/Referenced b/include/osg/Referenced index 452e0386a..0662b5f30 100644 --- a/include/osg/Referenced +++ b/include/osg/Referenced @@ -14,12 +14,12 @@ #ifndef OSG_REFERENCED #define OSG_REFERENCED 1 -#include +#include #include #include - #include + #if !defined(_OPENTHREADS_ATOMIC_USE_MUTEX) # define _OSG_REFERENCED_USE_ATOMIC_OPERATIONS #endif @@ -110,10 +110,10 @@ class OSG_EXPORT Referenced ObserverSet* getOrCreateObserverSet() const; /** Add a Observer that is observing this object, notify the Observer when this object gets deleted.*/ - void addObserver(Observer* observer) const { getOrCreateObserverSet()->addObserver(observer); } + void addObserver(Observer* observer) const; /** remove Observer that is observing this object.*/ - void removeObserver(Observer* observer) const { getOrCreateObserverSet()->removeObserver(observer); } + void removeObserver(Observer* observer) const; public: diff --git a/include/osg/observer_ptr b/include/osg/observer_ptr index ff1786a57..8ea9abd58 100644 --- a/include/osg/observer_ptr +++ b/include/osg/observer_ptr @@ -23,6 +23,131 @@ namespace osg { +#if 1 + +/** Smart pointer for observed objects, that automatically set pointers to them to null when they deleted. + * To use the observer_ptr<> robustly in multi-threaded applications it is recommend to access the pointer via + * the lock() method that passes back a ref_ptr<> that safely takes a reference to the object to prevent deletion + * during usage of the object. In certain conditions it may be safe to use the pointer directly without using lock(), + * which will confer a perfomance advantage, the conditions are: + * 1) The data structure is only accessed/deleted in single threaded/serial way. + * 2) The data strucutre is guarenteed by high level management of data strucutures and threads which avoid + * possible situations where the observer_ptr<>'s object may be deleted by one thread whilst being accessed + * by another. + * If you are in any doubt about whether it is safe to access the object safe then use + * ref_ptr<> observer_ptr<>.lock() combination. */ +template +class observer_ptr : public Observer +{ +public: + typedef T element_type; + observer_ptr() : _reference(0), _ptr(0) {} + + /** + * Create a observer_ptr from a ref_ptr. + */ + observer_ptr(const ref_ptr& rp) + { + _reference = rp.valid() ? rp->getOrCreateObserverSet() : 0; + _ptr = (_reference.valid() && _reference->getObserverdObject()!=0) ? rp.get() : 0; + } + + /** + * Create a observer_ptr from a raw pointer. For compatibility; + * the result might not be lockable. + */ + observer_ptr(T* rp) + { + _reference = rp ? rp->getOrCreateObserverSet() : 0; + _ptr = (_reference.valid() && _reference->getObserverdObject()!=0) ? rp : 0; + } + + observer_ptr(const observer_ptr& wp) : + _reference(wp._reference), + _ptr(wp._ptr) + { + } + + ~observer_ptr() + { + } + + observer_ptr& operator = (const observer_ptr& wp) + { + if (&wp==this) return *this; + + _reference = wp._reference; + _ptr = wp._ptr; + return *this; + } + + observer_ptr& operator = (const ref_ptr& rp) + { + _reference = rp.valid() ? rp->getOrCreateObserverSet() : 0; + _ptr = (_reference.valid() && _reference->getObserverdObject()!=0) ? rp.get() : 0; + return *this; + } + + observer_ptr& operator = (T* rp) + { + _reference = rp ? rp->getOrCreateObserverSet() : 0; + _ptr = (_reference.valid() && _reference->getObserverdObject()!=0) ? rp : 0; + return *this; + } + + /** + * Create a ref_ptr from a observer_ptr. The ref_ptr will be valid if the + * referenced object hasn't been deleted and has a ref count > 0. + */ + ref_ptr lock() const + { + if (!_reference) return ref_ptr(); + Referenced* obj = _reference->addRefLock(); + if (!obj) return ref_ptr(); + ref_ptr result(_ptr); + obj->unref_nodelete(); + return result; + } + + /** Comparison operators. These continue to work even after the + * observed object has been deleted. + */ + bool operator == (const observer_ptr& wp) const { return _reference == wp._reference; } + bool operator != (const observer_ptr& wp) const { return _reference != wp._reference; } + bool operator < (const observer_ptr& wp) const { return _reference < wp._reference; } + bool operator > (const observer_ptr& wp) const { return _reference > wp._reference; } + + // Non-strict interface, for compatibility + // comparison operator for const T*. + inline bool operator == (const T* ptr) const { return _ptr == ptr; } + inline bool operator != (const T* ptr) const { return _ptr != ptr; } + inline bool operator < (const T* ptr) const { return _ptr < ptr; } + inline bool operator > (const T* ptr) const { return _ptr > ptr; } + + // Convenience methods for operating on object, however, access is not automatically threadsafe. + // To make thread safe, one should either ensure at a high level + // that the object will not be deleted while operating on it, or + // by using the observer_ptr<>::lock() to get a ref_ptr<> that + // ensures the objects stay alive throughout all access to it. + + // Throw an error if _reference is null? + inline T& operator*() const { return *_ptr; } + inline T* operator->() const { return _ptr; } + + // get the raw C pointer + inline T* get() const { return (_reference.valid() && _reference->getObserverdObject()!=0) ? _ptr : 0; } + + inline bool operator!() const { return get() == 0; } + inline bool valid() const { return get() != 0; } + +protected: + + osg::ref_ptr _reference; + T* _ptr; +}; + +#else + // Internal class used to hold the "naked" pointer to the weakly // referenced object template @@ -179,7 +304,7 @@ public: /** * Create a observer_ptr from a ref_ptr. */ - observer_ptr(ref_ptr& rp) + observer_ptr(const ref_ptr& rp) { _reference = getWeakReference(rp.get()); } @@ -329,6 +454,8 @@ getWeakReference(const T* object) return result; } +#endif + } #endif diff --git a/src/osg/Observer.cpp b/src/osg/Observer.cpp index 54d8b2507..2ec2bbdbe 100644 --- a/src/osg/Observer.cpp +++ b/src/osg/Observer.cpp @@ -24,7 +24,8 @@ Observer::~Observer() { } -ObserverSet::ObserverSet() +ObserverSet::ObserverSet(const Referenced* observedObject): + _observedObject(const_cast(observedObject)) { //OSG_NOTICE<<"ObserverSet::ObserverSet() "< lock(*getObserverSetMutex()); + OpenThreads::ScopedLock lock(_mutex); _observers.insert(observer); } void ObserverSet::removeObserver(Observer* observer) { //OSG_NOTICE<<"ObserverSet::removeObserver("< lock(*getObserverSetMutex()); + OpenThreads::ScopedLock lock(_mutex); _observers.erase(observer); } +Referenced* ObserverSet::addRefLock() +{ + OpenThreads::ScopedLock lock(_mutex); + + if (!_observedObject) return 0; + + int refCount = _observedObject->ref(); + if (refCount == 1) + { + // The object is in the process of being deleted, but our + // objectDeleted() method hasn't been run yet (and we're + // blocking it -- and the final destruction -- with our lock). + _observedObject->unref_nodelete(); + return 0; + } + + return _observedObject; +} + void ObserverSet::signalObjectDeleted(void* ptr) { + OpenThreads::ScopedLock lock(_mutex); + for(Observers::iterator itr = _observers.begin(); itr != _observers.end(); ++itr) @@ -57,4 +79,7 @@ void ObserverSet::signalObjectDeleted(void* ptr) (*itr)->objectDeleted(ptr); } _observers.clear(); + + // reset the observed object so that we know that it's now detached. + _observedObject = 0; } diff --git a/src/osg/Referenced.cpp b/src/osg/Referenced.cpp index debfd89ac..994f577c8 100644 --- a/src/osg/Referenced.cpp +++ b/src/osg/Referenced.cpp @@ -242,9 +242,9 @@ Referenced::~Referenced() // delete the ObserverSet #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) - if (_observerSet.get()) delete static_cast(_observerSet.get()); + if (_observerSet.get()) static_cast(_observerSet.get())->unref(); #else - if (_observerSet) delete static_cast(_observerSet); + if (_observerSet) static_cast(_observerSet)->unref(); #endif #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) @@ -256,10 +256,16 @@ ObserverSet* Referenced::getOrCreateObserverSet() const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) ObserverSet* observerSet = static_cast(_observerSet.get()); - while (0 == observerSet) { - ObserverSet* newObserverSet = new ObserverSet; + while (0 == observerSet) + { + ObserverSet* newObserverSet = new ObserverSet(this); + newObserverSet->ref(); + if (!_observerSet.assign(newObserverSet, 0)) - delete newObserverSet; + { + newObserverSet->unref(); + } + observerSet = static_cast(_observerSet.get()); } return observerSet; @@ -267,17 +273,31 @@ ObserverSet* Referenced::getOrCreateObserverSet() const if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); - if (!_observerSet) _observerSet = new ObserverSet; + if (!_observerSet) + { + _observerSet = new ObserverSet(this); + _observerSet->ref(); + } return static_cast(_observerSet); } else { - if (!_observerSet) _observerSet = new ObserverSet; + if (!_observerSet) _observerSet = new ObserverSet(this); return static_cast(_observerSet); } #endif } +void Referenced::addObserver(Observer* observer) const +{ + getOrCreateObserverSet()->addObserver(observer); +} + +void Referenced::removeObserver(Observer* observer) const +{ + getOrCreateObserverSet()->removeObserver(observer); +} + void Referenced::signalObserversAndDelete(bool signalDelete, bool doDelete) const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) @@ -288,7 +308,6 @@ void Referenced::signalObserversAndDelete(bool signalDelete, bool doDelete) cons if (observerSet && signalDelete) { - OpenThreads::ScopedLock lock(*(observerSet->getObserverSetMutex())); observerSet->signalObjectDeleted(const_cast(this)); }