#include #include #include #include "GLTB/mutex.h" #include "GLTB/referencedobject.h" namespace gltb { /** * This mutex is used to prevent a race condition between * WeakReferenceCounter::_invalidate() and WeakReferenceCounter::decreaseReferenceCount() * as they both may try to delete the WeakReferenceCounter concurrently. */ Mutex *weakReferenceDeletionMutex=Mutex::_createMutex(); /** * This mutex is used to prevent WeakPtr::getRefPtr() from returning * a fresh RefPtr on a ReferencedObject that is currently being torn * down by its decreaseReferenceCount() method. * * @todo replace this with a r/w lock */ Mutex *referencedObjectDecreaseRefCountMutex=Mutex::_createMutex(); WeakReferenceCounter::WeakReferenceCounter() { object=0; referenceCount=0; } WeakReferenceCounter::~WeakReferenceCounter() { // std::cout << "WeakReferenceCounter::~WeakReferenceCounter()" << std::endl; } void WeakReferenceCounter::increaseReferenceCount() { atomicInc(&referenceCount); } void WeakReferenceCounter::decreaseReferenceCount() { // std::cout << "WeakReferenceCounter::decreaseReferenceCount()" << std::endl; int oldRefCount, newRefCount; weakReferenceDeletionMutex->lock(); do { oldRefCount=referenceCount; newRefCount=oldRefCount-1; } while(atomicCAS(&referenceCount,oldRefCount,newRefCount)!=oldRefCount); // test against newRefCount here because referenceCount may have been modified again after the compare_exchange // also, do not destroy this weak reference counter if it still references an existing object (might be called on again) // FIXME FIXME FIXME race condition with _invalidate if referenceCount==newReFCount==0: both functions then call delete if(newRefCount==0 && object==0) { delete this; } weakReferenceDeletionMutex->unlock(); } void WeakReferenceCounter::_invalidate() { // NOTE: weakReferenceDeletionMutex is aquired in ReferencedObject::decreaseReferenceCount() before calling // std::cout << "WeakReferenceCounter::invalidate() " << referenceCount << std::endl; object=0; // the reference object is passing away; if no weak references remain, the job of the weak counter is done, too if(referenceCount==0) { // std::cout << "delete this" << std::endl; delete this; } } ReferencedObject::ReferencedObject() { referenceCount=0; weakReferenceCounter=0; } ReferencedObject::~ReferencedObject() { // std::cout << "ReferencedObject::~ReferencedObject()" << std::endl; } void ReferencedObject::increaseReferenceCount() { //if(referenceCount>=0) { // referenceCount++; //} int oldRefCount, newRefCount; do { oldRefCount=referenceCount; if(oldRefCount<0) { return; } newRefCount=oldRefCount+1; } while(atomicCAS(&referenceCount,oldRefCount,newRefCount)!=oldRefCount); } void ReferencedObject::decreaseReferenceCount() { int oldRefCount, newRefCount; referencedObjectDecreaseRefCountMutex->lock(); do { oldRefCount=referenceCount; newRefCount=oldRefCount-1; // if reference count would be 0, set it to -1 instead to immediately block increments if(newRefCount==0) { newRefCount=-1; } } while(atomicCAS(&referenceCount,oldRefCount,newRefCount)!=oldRefCount); //assert(newRefCount>=0); // test against newRefCount here because referenceCount may have been modified again after the compare_exchange if(newRefCount==-1) { // clear weak reference pointer, if it exists if(weakReferenceCounter!=(WeakReferenceCounter*)0) { { weakReferenceDeletionMutex->lock(); if(weakReferenceCounter!=(WeakReferenceCounter*)0) { ((WeakReferenceCounter*)weakReferenceCounter)->_invalidate(); } weakReferenceDeletionMutex->unlock(); } } // unlock mutex early, because object destruction can be long and complex referencedObjectDecreaseRefCountMutex->unlock(); // delete object itself delete this; } else { referencedObjectDecreaseRefCountMutex->unlock(); } } WeakReferenceCounter *ReferencedObject::_getWeakReferenceCounter() { if(weakReferenceCounter==(WeakReferenceCounter*)0) { /* * If the weak reference counter does not yet exist, we might * enter a race to create it here (with another thread trying * to do the same). Thus, setting the counter has to be done * with a compare_exchange and if it fails, we have to assume * that the other thread was faster and destroy our counter * to prevent a memory leak. */ WeakReferenceCounter *newCounter=new WeakReferenceCounter; newCounter->object=this; WeakReferenceCounter *oldCounter=0; #if !defined(_M_X64) && !defined(__x86_64__) if(atomicCAS((unsigned int *)&weakReferenceCounter,(unsigned int)oldCounter,(unsigned int)newCounter)!=(unsigned int)oldCounter) { // another thread was faster with the allocation #else if(atomicCAS((unsigned long long *)&weakReferenceCounter,(unsigned long long)oldCounter,(unsigned long long)newCounter)!=(unsigned long long)oldCounter) { // another thread was faster with the allocation #endif delete newCounter; } } // std::cout << "ReferencedObject: weakReferenceCounter=" << std::setw(16) << std::hex << (void*)weakReferenceCounter << std::endl; return weakReferenceCounter; } }