/* -*-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_REFERENCED #define OSG_REFERENCED 1 #include #include #include #include #if !defined(_OPENTHREADS_ATOMIC_USE_MUTEX) # define _OSG_REFERENCED_USE_ATOMIC_OPERATIONS #endif namespace osg { // forward declare, declared after Referenced below. class DeleteHandler; class Observer; class ObserverSet; /** template class to help enforce static initialization order. */ template struct depends_on { depends_on() { M(); } }; /** Base class for providing reference counted objects.*/ class OSG_EXPORT Referenced { public: Referenced(); explicit Referenced(bool threadSafeRefUnref); Referenced(const Referenced&); inline Referenced& operator = (const Referenced&) { return *this; } /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/ virtual void setThreadSafeRefUnref(bool threadSafe); /** Get whether a mutex is used to ensure ref() and unref() are thread safe.*/ #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) bool getThreadSafeRefUnref() const { return true; } #else bool getThreadSafeRefUnref() const { return _refMutex!=0; } #endif /** Get the mutex used to ensure thread safety of ref()/unref(). */ #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) OpenThreads::Mutex* getRefMutex() const { return getGlobalReferencedMutex(); } #else OpenThreads::Mutex* getRefMutex() const { return _refMutex; } #endif /** Get the optional global Referenced mutex, this can be shared between all osg::Referenced.*/ static OpenThreads::Mutex* getGlobalReferencedMutex(); /** Increment the reference count by one, indicating that this object has another pointer which is referencing it.*/ inline int ref() const; /** Decrement the reference count by one, indicating that a pointer to this object is referencing it. If the reference count goes to zero, it is assumed that this object is no longer referenced and is automatically deleted.*/ inline int unref() const; /** Decrement the reference count by one, indicating that a pointer to this object is referencing it. However, do not delete it, even if ref count goes to 0. Warning, unref_nodelete() should only be called if the user knows exactly who will be responsible for, one should prefer unref() over unref_nodelete() as the latter can lead to memory leaks.*/ int unref_nodelete() const; /** Return the number of pointers currently referencing this object. */ inline int referenceCount() const { return _refCount; } /** Get the ObserverSet if one is attached, otherwise return NULL.*/ ObserverSet* getObserverSet() const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) return static_cast(_observerSet.get()); #else return static_cast(_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 addObserver(Observer* observer) const; /** Remove Observer that is observing this object.*/ void removeObserver(Observer* observer) const; public: /** Set whether reference counting should use a mutex for thread safe reference counting.*/ static void setThreadSafeReferenceCounting(bool enableThreadSafeReferenceCounting); /** Get whether reference counting is active.*/ static bool getThreadSafeReferenceCounting(); friend class DeleteHandler; /** Set a DeleteHandler to which deletion of all referenced counted objects * will be delegated.*/ static void setDeleteHandler(DeleteHandler* handler); /** Get a DeleteHandler.*/ static DeleteHandler* getDeleteHandler(); protected: virtual ~Referenced(); void signalObserversAndDelete(bool signalDelete, bool doDelete) const; void deleteUsingDeleteHandler() const; #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) mutable OpenThreads::AtomicPtr _observerSet; mutable OpenThreads::Atomic _refCount; #else mutable OpenThreads::Mutex* _refMutex; mutable int _refCount; mutable void* _observerSet; #endif }; inline int Referenced::ref() const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) return ++_refCount; #else if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); return ++_refCount; } else { return ++_refCount; } #endif } inline int Referenced::unref() const { int newRef; #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) newRef = --_refCount; bool needDelete = (newRef == 0); #else bool needDelete = false; if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); newRef = --_refCount; needDelete = newRef==0; } else { newRef = --_refCount; needDelete = newRef==0; } #endif if (needDelete) { signalObserversAndDelete(true,true); } return newRef; } // intrusive_ptr_add_ref and intrusive_ptr_release allow // use of osg Referenced classes with boost::intrusive_ptr inline void intrusive_ptr_add_ref(Referenced* p) { p->ref(); } inline void intrusive_ptr_release(Referenced* p) { p->unref(); } } #endif