Refactored the osg::Referenced observerset code so that it now uses a dedicated ObserverSet class,

which utilises a global recursive mutex that is dedicated to manage Observer and ObserverSet.

The new global mutex for observers avoids problems with deadlocks that were occurring previously when
an osg::Refenced object was being deleted at the same time as on osg::ObserverNodePath.
This commit is contained in:
Robert Osfield
2010-02-18 21:21:12 +00:00
parent 6196652ef0
commit b09757bdb8
7 changed files with 234 additions and 215 deletions

View File

@@ -11,28 +11,63 @@
* OpenSceneGraph Public License for more details.
*/
#ifndef OSG_OCCLUDER
#define OSG_OCCLUDER 1
#ifndef OSG_OBSERVER
#define OSG_OBSERVER 1
#include <OpenThreads/Mutex>
#include <osg/Export>
#include <set>
namespace osg {
/** Observer base class for tracking when objects are unreferenced (there reference count goes to 0) and are being deleted.*/
class Observer
class OSG_EXPORT Observer
{
public:
virtual ~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; }
/** Get the optional global observer mutex, this can be shared between all osg::Observer.*/
static OpenThreads::Mutex* getGlobalObserverMutex();
/** 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*) {}
inline OpenThreads::Mutex* getObserverMutex() const { return osg::Observer::getGlobalObserverMutex(); }
/** 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*) {}
};
/** Class used by osg::Referenced to track the observers assoicated with it.*/
class OSG_EXPORT ObserverSet
{
public:
ObserverSet();
~ObserverSet();
inline OpenThreads::Mutex* getObserverSetMutex() const { return osg::Observer::getGlobalObserverMutex(); }
void addObserver(Observer* observer);
void removeObserver(Observer* observer);
void signalObjectUnreferenced(void* ptr);
void signalObjectDeleted(void* ptr);
typedef std::set<Observer*> Observers;
Observers& getObservers() { return _observers; }
const Observers& getObservers() const { return _observers; }
protected:
Observers _observers;
};
}

View File

@@ -67,7 +67,6 @@ class OSG_EXPORT ObserverNodePath : public osg::Observer
virtual bool objectUnreferenced(void* ptr);
mutable OpenThreads::Mutex _mutex;
osg::NodePath _nodePath;
bool _valid;
};

View File

@@ -14,8 +14,7 @@
#ifndef OSG_REFERENCED
#define OSG_REFERENCED 1
// When building OSG with Java need to derive from Noodle::CBridgable class,
#include <osg/Export>
#include <osg/Observer>
#include <OpenThreads/ScopedLock>
#include <OpenThreads/Mutex>
@@ -30,6 +29,7 @@ namespace osg {
// forward declare, declared after Referenced below.
class DeleteHandler;
class Observer;
class ObserverSet;
/** template class to help enforce static initialization order. */
template <typename T, T M()>
@@ -95,11 +95,25 @@ class OSG_EXPORT Referenced
/** Return the number pointers currently referencing this object. */
inline int referenceCount() const { return _refCount; }
/** Add a Observer that is observing this object, notify the Observer when this object gets deleted.*/
void addObserver(Observer* observer) const;
/** Get the ObserverSet if one is attached, otherwise return NULL.*/
ObserverSet* getObserverSet() const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
return static_cast<ObserverSet*>(_observerSet.get());
#else
return static_cast<ObserverSet*>(_observerSet);
#endif
}
/** Get the ObserverSet if one is attached, otherwise create an ObserverSet, attach it, then return this newly created ObserverSet.*/
ObserverSet* getOrCreateObserverSet() const;
/** Add a Observer that is observing this object, notify the Observer when this object gets deleted.*/
void removeObserver(Observer* observer) const;
void addObserver(Observer* observer) const { getOrCreateObserverSet()->addObserver(observer); }
/** remove Observer that is observing this object.*/
void removeObserver(Observer* observer) const { getOrCreateObserverSet()->removeObserver(observer); }
public:
@@ -128,9 +142,7 @@ class OSG_EXPORT Referenced
void deleteUsingDeleteHandler() const;
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
struct ObserverSetData;
mutable OpenThreads::AtomicPtr _observerSetDataPtr;
mutable OpenThreads::AtomicPtr _observerSet;
mutable OpenThreads::Atomic _refCount;
#else
@@ -139,7 +151,7 @@ class OSG_EXPORT Referenced
mutable int _refCount;
mutable void* _observers;
mutable void* _observerSet;
#endif
};
@@ -163,17 +175,17 @@ 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<OpenThreads::Mutex> lock(*_refMutex);
needDelete = (--_refCount)<=0;
needDelete = (--_refCount)==0;
}
else
{
needDelete = (--_refCount)<=0;
needDelete = (--_refCount)==0;
}
#endif