/* -*-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_OBSERVER_PTR #define OSG_OBSERVER_PTR #include #include #include #include #include namespace osg { // Internal class used to hold the "naked" pointer to the weakly // referenced object template struct WeakReference : public Observer, public Referenced { WeakReference(const T* ptr) : _ptr(const_cast(ptr)) {} void objectDeleted(void*); /** * "Lock" a Referenced object i.e., protect it from being deleted * by incrementing its reference count. * * returns null if object doesn't exist anymore */ virtual T* addRefLock() { OpenThreads::ScopedLock lock(_mutex); if (!_ptr) return 0; int refCount = _ptr->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). _ptr->unref_nodelete(); return 0; } return _ptr; } OpenThreads::Mutex _mutex; T* _ptr; }; // Reference object for observer_ptr that was initialized with an // "unsafe" object e.g., a stack-allocated object. template struct UnsafeWeakReference : public WeakReference { UnsafeWeakReference(const T* ptr) : WeakReference(ptr) {} // Don't even touch the ref count of an unsafe object T* addRefLock() { OSG_WARN << "tried to lock an unsafe observer_ptr."; return 0; } }; /** Delete the WeakReference object, if necessary. The WeakReference should be deleted when the last observer_ptr pointing to it goes out of scope; this avoids a memory leak. A race must be prevented between this function and WeakReference::objectDeleted(). If the referenced object is being deleted at the same time as this function is called, we can't prevent objectDeleted() from being run, so we need to leave the WeakReference in a valid state and let objectDeleted() delete it. */ template void maybeDelete(WeakReference* weakRef) { if (!weakRef || weakRef->unref_nodelete() > 0) return; // No other observer_ptrs hold weakRef, so clean up. bool doDelete = false; { OpenThreads::ScopedLock lock(weakRef->_mutex); if (!weakRef->_ptr) { // The object has already been deleted; it's OK to delete weakRef. doDelete = true; } else { // Carefully remove weakRef as an observer. UnsafeWeakReference* unsafe = dynamic_cast*>(weakRef); if (!unsafe && weakRef->_ptr->ref() == 1) { // The referenced object is being deleted, so the // Observer method must delete the weak reference. weakRef->_ptr->unref_nodelete(); weakRef->_ptr = 0; } else { // The referenced object won't be deleted until we // decrement the reference count, so go ahead and // remove the observer. doDelete = true; weakRef->_ptr->removeObserver(weakRef); if (!unsafe) weakRef->_ptr->unref_nodelete(); } } } if (doDelete) delete weakRef; } /** * Get a weak reference from a Referenced. This returns a * WeakReference object with the ref count bumped. We must already * hold a reference to the Referenced object so it won't get * deleted during this process. */ template WeakReference* getWeakReference(const T* object); /** 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) {} /** * Create a observer_ptr from a ref_ptr. */ observer_ptr(ref_ptr& rp) { _reference = getWeakReference(rp.get()); } /** * Create a observer_ptr from a raw pointer. For compatibility; * the result might not be lockable. */ observer_ptr(T* rp) { _reference = getWeakReference(rp); } observer_ptr(const observer_ptr& wp) : _reference(wp._reference) { if (_reference) _reference->ref(); } ~observer_ptr() { maybeDelete(_reference); } observer_ptr& operator = (const observer_ptr& wp) { if (wp._reference) wp._reference->ref(); maybeDelete(_reference); _reference = wp._reference; return *this; } observer_ptr& operator = (const ref_ptr& rp) { WeakReference* tmp = getWeakReference(rp.get()); maybeDelete(_reference); _reference = tmp; return *this; } observer_ptr& operator = (T* rp) { WeakReference* tmp = getWeakReference(rp); maybeDelete(_reference); _reference = tmp; 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(); T* obj = _reference->addRefLock(); if (!obj) return ref_ptr(); ref_ptr result(obj); 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 { if (!_reference) return !ptr; else return (_reference->_ptr==ptr); } inline bool operator != (const T* ptr) const { if (!_reference) return ptr; else return (_reference->_ptr!=ptr); } inline bool operator < (const T* ptr) const { return (_reference && _reference->_ptr (const T* ptr) const { return (_reference && _reference->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 *_reference->_ptr; } inline T* operator->() const { return _reference? _reference->_ptr : 0; } // get the raw C pointer inline T* get() const { return _reference ? _reference->_ptr : 0; } inline bool operator!() const { return !_reference || !_reference->_ptr; } inline bool valid() const { return _reference && _reference->_ptr; } protected: // The pointer to the WeakReference is not kept in a ref_ptr so that // its deletion can be managed explicitly. WeakReference* _reference; }; template void WeakReference::objectDeleted(void*) { bool deleteNeeded = false; { OpenThreads::ScopedLock lock(_mutex); if (!_ptr) { // The last weak reference was deleted after the last // reference, but the observer hasn't run yet. The // observer can't be prevented from running, so it // must delete itself. deleteNeeded = true; } else { _ptr = 0; } } if (deleteNeeded) delete this; } template WeakReference* getWeakReference(const T* object) { if (!object) return 0; WeakReference* result = 0; ObserverSet* setData = object->getOrCreateObserverSet(); OpenThreads::ScopedLock lock(*setData->getObserverSetMutex()); ObserverSet::Observers &observers = setData->getObservers(); for (ObserverSet::Observers::iterator itr = observers.begin(), end = observers.end(); itr != end; ++itr) { if ((result = dynamic_cast*>(*itr))) { int weakRefCount = result->ref(); if (weakRefCount == 1) { // The last weak reference disappeared, but hasn't // been removed from the observers list yet. result->unref_nodelete(); } else { return result; } } } if (object->referenceCount() > 0) result = new WeakReference(object); else result = new UnsafeWeakReference(object); result->ref(); observers.insert(result); return result; } } #endif