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:
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user