/* -*-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. */ #include #include #include #include #include #include #include #include #include #include #include namespace osg { //#define ENFORCE_THREADSAFE //#define DEBUG_OBJECT_ALLOCATION_DESTRUCTION // specialized smart pointer, used to get round auto_ptr<>'s lack of the destructor reseting itself to 0. template struct ResetPointer { ResetPointer(): _ptr(0) {} ResetPointer(T* ptr): _ptr(ptr) {} ~ResetPointer() { delete _ptr; _ptr = 0; } inline ResetPointer& operator = (T* ptr) { if (_ptr==ptr) return *this; delete _ptr; _ptr = ptr; return *this; } void reset(T* ptr) { if (_ptr==ptr) return; delete _ptr; _ptr = ptr; } inline T& operator*() { return *_ptr; } inline const T& operator*() const { return *_ptr; } inline T* operator->() { return _ptr; } inline const T* operator->() const { return _ptr; } T* get() { return _ptr; } const T* get() const { return _ptr; } T* _ptr; }; typedef ResetPointer DeleteHandlerPointer; typedef ResetPointer GlobalMutexPointer; OpenThreads::Mutex* Referenced::getGlobalReferencedMutex() { static GlobalMutexPointer s_ReferencedGlobalMutext = new OpenThreads::Mutex; return s_ReferencedGlobalMutext.get(); } typedef std::set ObserverSet; #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) struct Referenced::ObserverSetData { OpenThreads::Mutex _mutex; ObserverSet _observers; }; #endif #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) static bool s_useThreadSafeReferenceCounting = getenv("OSG_THREAD_SAFE_REF_UNREF")!=0; #endif // static std::auto_ptr s_deleteHandler(0); static DeleteHandlerPointer s_deleteHandler(0); static ApplicationUsageProxy Referenced_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_THREAD_SAFE_REF_UNREF",""); void Referenced::setThreadSafeReferenceCounting(bool enableThreadSafeReferenceCounting) { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) s_useThreadSafeReferenceCounting = enableThreadSafeReferenceCounting; #endif } bool Referenced::getThreadSafeReferenceCounting() { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) return true; #else return s_useThreadSafeReferenceCounting; #endif } void Referenced::setDeleteHandler(DeleteHandler* handler) { s_deleteHandler.reset(handler); } DeleteHandler* Referenced::getDeleteHandler() { return s_deleteHandler.get(); } #ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION OpenThreads::Mutex& getNumObjectMutex() { static OpenThreads::Mutex s_numObjectMutex; return s_numObjectMutex; } static int s_numObjects = 0; #endif Referenced::Referenced(): #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) _observerSetDataPtr(0), _refCount(0) #else _refMutex(0), _refCount(0), _observers(0) #endif { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) #ifndef ENFORCE_THREADSAFE if (s_useThreadSafeReferenceCounting) #endif _refMutex = new OpenThreads::Mutex; #endif #ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION { OpenThreads::ScopedLock lock(getNumObjectMutex()); ++s_numObjects; osg::notify(osg::NOTICE)<<"Object created, total num="< lock(getNumObjectMutex()); ++s_numObjects; osg::notify(osg::NOTICE)<<"Object created, total num="< lock(getNumObjectMutex()); ++s_numObjects; osg::notify(osg::NOTICE)<<"Object created, total num="< lock(getNumObjectMutex()); --s_numObjects; osg::notify(osg::NOTICE)<<"Object deleted, total num="<0) { notify(WARN)<<"Warning: deleting still referenced object "<(_observers); for(ObserverSet::iterator itr = os->begin(); itr != os->end(); ++itr) { (*itr)->objectDeleted(this); } delete os; _observers = 0; } if (_refMutex) { OpenThreads::Mutex* tmpMutexPtr = _refMutex; _refMutex = 0; delete tmpMutexPtr; } #else ObserverSetData* observerSetData = static_cast(_observerSetDataPtr.get()); if (observerSetData) { for(ObserverSet::iterator itr = observerSetData->_observers.begin(); itr != observerSetData->_observers.end(); ++itr) { (*itr)->objectDeleted(this); } _observerSetDataPtr.assign(0, observerSetData); delete observerSetData; } #endif } void Referenced::setThreadSafeRefUnref(bool threadSafe) { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) if (threadSafe) { if (!_refMutex) { // we want thread safe ref()/unref() so assign a mutex _refMutex = new OpenThreads::Mutex; } } else { if (_refMutex) { // we don't want thread safe ref()/unref() so remove any assigned mutex OpenThreads::Mutex* tmpMutexPtr = _refMutex; _refMutex = 0; delete tmpMutexPtr; } } #endif } void Referenced::unref_nodelete() const { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); --_refCount; } else { --_refCount; } #else --_refCount; #endif } void Referenced::addObserver(Observer* observer) const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) ObserverSetData* observerSetData = static_cast(_observerSetDataPtr.get()); while (0 == observerSetData) { ObserverSetData* newObserverSetData = new ObserverSetData; if (!_observerSetDataPtr.assign(newObserverSetData, 0)) delete newObserverSetData; observerSetData = static_cast(_observerSetDataPtr.get()); } OpenThreads::ScopedLock lock(observerSetData->_mutex); observerSetData->_observers.insert(observer); #else if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); if (!_observers) _observers = new ObserverSet; if (_observers) static_cast(_observers)->insert(observer); } else { if (!_observers) _observers = new ObserverSet; if (_observers) static_cast(_observers)->insert(observer); } #endif } void Referenced::removeObserver(Observer* observer) const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) ObserverSetData* observerSetData = static_cast(_observerSetDataPtr.get()); if (observerSetData) { OpenThreads::ScopedLock lock(observerSetData->_mutex); observerSetData->_observers.erase(observer); } #else if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); if (_observers) static_cast(_observers)->erase(observer); } else { if (_observers) static_cast(_observers)->erase(observer); } #endif } void Referenced::deleteUsingDeleteHandler() const { getDeleteHandler()->requestDelete(this); } } // end of namespace osg