diff --git a/include/osg/Observer b/include/osg/Observer new file mode 100644 index 000000000..8faaa41a2 --- /dev/null +++ b/include/osg/Observer @@ -0,0 +1,40 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSG_OCCLUDER +#define OSG_OCCLUDER 1 + +namespace osg { + +/** Observer base class for tracking when objects are unreferenced (there reference count goes to 0) and are being deleted.*/ +class Observer +{ +public: + virtual ~Observer() {} + + /** objectUnreferenced(void*) is called when the observed object's referenced count goes to zero, indicating that + * the object will be deleted unless a new reference is made to it. If you wish to prevent deletion of the object + * then it's reference count should be incremented such as via taking a ref_ptr<> to it, if no refernce is taken + * by any of the observers of the object then the object will be deleted, and objectDeleted will in turn be called. + * return true if the Observer wishes to removed from the oberseved objects observer set.*/ + virtual bool objectUnreferenced(void*) { return false; } + + /** objectDeleted is called when the observed object is about to be deleted. The observer will be automatically + * removed from the observerd objects observer set so there is no need for the objectDeleted implementation + * to call removeObserver() on the observed object. */ + virtual void objectDeleted(void*) {} +}; + +} + +#endif \ No newline at end of file diff --git a/include/osg/Referenced b/include/osg/Referenced index e95b27037..3a0b776cc 100644 --- a/include/osg/Referenced +++ b/include/osg/Referenced @@ -122,7 +122,9 @@ class OSG_EXPORT Referenced protected: virtual ~Referenced(); - + + void signalObserversAndDelete(bool signalUnreferened, bool signalDelete, bool doDelete) const; + void deleteUsingDeleteHandler() const; #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) @@ -161,26 +163,23 @@ inline void Referenced::ref() const inline void Referenced::unref() const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) - bool needDelete = (--_refCount == 0); + bool needDelete = (--_refCount <= 0); #else bool needDelete = false; if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); - --_refCount; - needDelete = _refCount<=0; + needDelete = (--_refCount)<=0; } else { - --_refCount; - needDelete = _refCount<=0; + needDelete = (--_refCount)<=0; } #endif if (needDelete) { - if (getDeleteHandler()) deleteUsingDeleteHandler(); - else delete this; + signalObserversAndDelete(true,true,true); } } diff --git a/include/osg/observer_ptr b/include/osg/observer_ptr index 464f37fab..a82d76fd8 100644 --- a/include/osg/observer_ptr +++ b/include/osg/observer_ptr @@ -15,17 +15,22 @@ #define OSG_OBSERVER_PTR #include +#include +#include namespace osg { -class Observer -{ -public: - virtual ~Observer() {} - virtual void objectDeleted(void*) {} -}; - -/** Smart pointer for observed objects, that automatically set pointers to them to null when they deleted.*/ +/** Smart pointer for observed objects, that automatically set pointers to them to null when they deleted. + * To use the observer_ptr<> robustly in mulit-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 { @@ -40,9 +45,10 @@ class observer_ptr : public Observer inline observer_ptr& operator = (const observer_ptr& rp) { + OpenThreads::ScopedLock lock(_mutex); if (_ptr==rp._ptr) return *this; if (_ptr) _ptr->removeObserver(this); - + _ptr = rp._ptr; if (_ptr) _ptr->addObserver(this); return *this; @@ -50,20 +56,26 @@ class observer_ptr : public Observer inline observer_ptr& operator = (T* ptr) { + OpenThreads::ScopedLock lock(_mutex); if (_ptr==ptr) return *this; if (_ptr) _ptr->removeObserver(this); - + _ptr = ptr; if (_ptr) _ptr->addObserver(this); return *this; } - - virtual void objectDeleted(void*) + + // robust thread safe access to pointer + ref_ptr lock() const { - _ptr = 0; + OpenThreads::ScopedLock lock(_mutex); + return ref_ptr(_ptr); } + // get the raw C pointer + inline T* get() const { return _ptr; } + // comparison operators for observer_ptr. inline bool operator == (const observer_ptr& rp) const { return (_ptr==rp._ptr); } inline bool operator != (const observer_ptr& rp) const { return (_ptr!=rp._ptr); } @@ -76,17 +88,26 @@ class observer_ptr : public Observer inline bool operator < (const T* ptr) const { return (_ptr (const T* ptr) const { return (_ptr>ptr); } + // convinience methods for operating on object, however, access to not automatically threadsafe + // to make thread safe one should either ensure a high level that object will not be deleted + // which 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. inline T& operator*() const { return *_ptr; } inline T* operator->() const { return _ptr; } - inline T* get() const { return _ptr; } inline bool operator!() const { return _ptr==0L; } - inline bool valid() const { return _ptr!=0L; } - - private: - T* _ptr; + protected: + + virtual void objectDeleted(void*) + { + OpenThreads::ScopedLock lock(_mutex); + _ptr = 0; + } + + mutable OpenThreads::Mutex _mutex; + T* _ptr; }; } diff --git a/src/osg/Referenced.cpp b/src/osg/Referenced.cpp index 45ab71429..c18ee9e5d 100644 --- a/src/osg/Referenced.cpp +++ b/src/osg/Referenced.cpp @@ -235,42 +235,125 @@ Referenced::~Referenced() notify(WARN)<<" the final reference count was "<<_refCount<<", memory corruption possible."<(_observers); - for(ObserverSet::iterator itr = os->begin(); - itr != os->end(); - ++itr) - { - (*itr)->objectDeleted(this); - } - delete os; - _observers = 0; + // tell all observers that we have been unreferenced so that they + // can do cleans up or add their own reference to prevent deletion. + #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) + ObserverSet* os = static_cast(_observers); + if (os) + { + if (_refMutex) + { + OpenThreads::ScopedLock lock(*_refMutex); + for(ObserverSet::iterator itr = os->begin(); + itr != os->end(); + ) + { + if ((*itr)->objectUnreferenced(const_cast(this))) + { + ObserverSet::iterator orig_itr = itr; + ++itr; + os.erase(orig_itr); + } + else + { + ++itr; + } + } + } + else + { + for(ObserverSet::iterator itr = os->begin(); + itr != os->end(); + ) + { + if ((*itr)->objectUnreferenced(const_cast(this))) + { + ObserverSet::iterator orig_itr = itr; + ++itr; + os.erase(orig_itr); + } + else + { + ++itr; + } + } + } + } + #else + ObserverSetData* observerSetData = static_cast(_observerSetDataPtr.get()); + if (observerSetData) + { + OpenThreads::ScopedLock lock(observerSetData->_mutex); + for(ObserverSet::iterator itr = observerSetData->_observers.begin(); + itr != observerSetData->_observers.end(); + ) + { + if ((*itr)->objectUnreferenced(const_cast(this))) + { + ObserverSet::iterator orig_itr = itr; + ++itr; + observerSetData->_observers.erase(orig_itr); + } + else + { + ++itr; + } + } + } + #endif } - if (_refMutex) + if (_refCount!=0) return; + + if (signalDelete) { - OpenThreads::Mutex* tmpMutexPtr = _refMutex; - _refMutex = 0; - delete tmpMutexPtr; + // tell all observers that we being delete so that they + // can do cleans up and remove any references they have. + #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) + ObserverSet* os = static_cast(_observers); + if (os) + { + for(ObserverSet::iterator itr = os->begin(); + itr != os->end(); + ++itr) + { + (*itr)->objectDeleted(const_cast(this)); + } + delete os; + _observers = 0; + } + #else + ObserverSetData* observerSetData = static_cast(_observerSetDataPtr.get()); + if (observerSetData) + { + for(ObserverSet::iterator itr = observerSetData->_observers.begin(); + itr != observerSetData->_observers.end(); + ++itr) + { + (*itr)->objectDeleted(const_cast(this)); + } + _observerSetDataPtr.assign(0, observerSetData); + delete observerSetData; + } + #endif } -#else - ObserverSetData* observerSetData = static_cast(_observerSetDataPtr.get()); - if (observerSetData) + + if (doDelete &&_refCount<=0) { - for(ObserverSet::iterator itr = observerSetData->_observers.begin(); - itr != observerSetData->_observers.end(); - ++itr) - { - (*itr)->objectDeleted(this); - } - _observerSetDataPtr.assign(0, observerSetData); - delete observerSetData; + if (getDeleteHandler()) deleteUsingDeleteHandler(); + else delete this; } -#endif } + void Referenced::setThreadSafeRefUnref(bool threadSafe) { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) @@ -299,18 +382,24 @@ void Referenced::setThreadSafeRefUnref(bool threadSafe) void Referenced::unref_nodelete() const { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) + bool needUnreferencedSignal = false; if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); - --_refCount; + needUnreferencedSignal = (--_refCount)<=0; } else { - --_refCount; + needUnreferencedSignal = (--_refCount)<=0; } #else - --_refCount; + bool needUnreferencedSignal = (--_refCount)<=0; #endif + + if (needUnreferencedSignal) + { + signalObserversAndDelete(true,false,false); + } } void Referenced::addObserver(Observer* observer) const