/* -*-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 resetting 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(); } // helper class for forcing the global mutex to be constructed when the library is loaded. struct InitGlobalMutexes { InitGlobalMutexes() { Referenced::getGlobalReferencedMutex(); } }; static InitGlobalMutexes s_initGlobalMutexes; // static std::auto_ptr s_deleteHandler(0); static DeleteHandlerPointer s_deleteHandler(0); 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) _observerSet(0), _refCount(0) #else _refMutex(0), _refCount(0), _observerSet(0) #endif { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) _refMutex = new OpenThreads::Mutex; #endif #ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION { OpenThreads::ScopedLock lock(getNumObjectMutex()); ++s_numObjects; printf("Object created, total num=%d\n",s_numObjects); } #endif } Referenced::Referenced(bool /*threadSafeRefUnref*/): #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) _observerSet(0), _refCount(0) #else _refMutex(0), _refCount(0), _observerSet(0) #endif { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) _refMutex = new OpenThreads::Mutex; #endif #ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION { OpenThreads::ScopedLock lock(getNumObjectMutex()); ++s_numObjects; printf("Object created, total num=%d\n",s_numObjects); } #endif } Referenced::Referenced(const Referenced&): #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) _observerSet(0), _refCount(0) #else _refMutex(0), _refCount(0), _observerSet(0) #endif { #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) _refMutex = new OpenThreads::Mutex; #endif #ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION { OpenThreads::ScopedLock lock(getNumObjectMutex()); ++s_numObjects; printf("Object created, total num=%d\n",s_numObjects); } #endif } Referenced::~Referenced() { #ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION { OpenThreads::ScopedLock lock(getNumObjectMutex()); --s_numObjects; printf("Object created, total num=%d\n",s_numObjects); } #endif if (_refCount>0) { OSG_WARN<<"Warning: deleting still referenced object "<(_observerSet.get())->unref(); #else if (_observerSet) static_cast(_observerSet)->unref(); #endif #if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) if (_refMutex) delete _refMutex; #endif } ObserverSet* Referenced::getOrCreateObserverSet() const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) ObserverSet* observerSet = static_cast(_observerSet.get()); while (0 == observerSet) { ObserverSet* newObserverSet = new ObserverSet(this); newObserverSet->ref(); if (!_observerSet.assign(newObserverSet, 0)) { newObserverSet->unref(); } observerSet = static_cast(_observerSet.get()); } return observerSet; #else if (_refMutex) { OpenThreads::ScopedLock lock(*_refMutex); if (!_observerSet) { _observerSet = new ObserverSet(this); static_cast(_observerSet)->ref(); } return static_cast(_observerSet); } else { if (!_observerSet) { _observerSet = new ObserverSet(this); static_cast(_observerSet)->ref(); } return static_cast(_observerSet); } #endif } void Referenced::addObserver(Observer* observer) const { getOrCreateObserverSet()->addObserver(observer); } void Referenced::removeObserver(Observer* observer) const { getOrCreateObserverSet()->removeObserver(observer); } void Referenced::signalObserversAndDelete(bool signalDelete, bool doDelete) const { #if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS) ObserverSet* observerSet = static_cast(_observerSet.get()); #else ObserverSet* observerSet = static_cast(_observerSet); #endif if (observerSet && signalDelete) { observerSet->signalObjectDeleted(const_cast(this)); } if (doDelete) { if (_refCount!=0) OSG_NOTICE<<"Warning Referenced::signalObserversAndDelete(,,) doing delete with _refCount="<<_refCount< lock(*_refMutex); return --_refCount; } else { return --_refCount; } #endif } void Referenced::deleteUsingDeleteHandler() const { getDeleteHandler()->requestDelete(this); } } // end of namespace osg