nuclear@3: /************************************************************************************ nuclear@3: nuclear@3: PublicHeader: Kernel nuclear@3: Filename : OVR_RefCount.h nuclear@3: Content : Reference counting implementation headers nuclear@3: Created : September 19, 2012 nuclear@3: Notes : nuclear@3: nuclear@3: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@3: nuclear@3: Use of this software is subject to the terms of the Oculus license nuclear@3: agreement provided at the time of installation or download, or which nuclear@3: otherwise accompanies this software in either electronic or hard copy form. nuclear@3: nuclear@3: ************************************************************************************/ nuclear@3: nuclear@3: #ifndef OVR_RefCount_h nuclear@3: #define OVR_RefCount_h nuclear@3: nuclear@3: #include "OVR_Types.h" nuclear@3: #include "OVR_Allocator.h" nuclear@3: nuclear@3: namespace OVR { nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Reference Counting nuclear@3: nuclear@3: // There are three types of reference counting base classes: nuclear@3: // nuclear@3: // RefCountBase - Provides thread-safe reference counting (Default). nuclear@3: // RefCountBaseNTS - Non Thread Safe version of reference counting. nuclear@3: nuclear@3: nuclear@3: // ***** Declared classes nuclear@3: nuclear@3: template nuclear@3: class RefCountBase; nuclear@3: template nuclear@3: class RefCountBaseNTS; nuclear@3: nuclear@3: class RefCountImpl; nuclear@3: class RefCountNTSImpl; nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Implementation For Reference Counting nuclear@3: nuclear@3: // RefCountImplCore holds RefCount value and defines a few utility nuclear@3: // functions shared by all implementations. nuclear@3: nuclear@3: class RefCountImplCore nuclear@3: { nuclear@3: protected: nuclear@3: volatile int RefCount; nuclear@3: nuclear@3: public: nuclear@3: // RefCountImpl constructor always initializes RefCount to 1 by default. nuclear@3: OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { } nuclear@3: nuclear@3: // Need virtual destructor nuclear@3: // This: 1. Makes sure the right destructor's called. nuclear@3: // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() nuclear@3: virtual ~RefCountImplCore(); nuclear@3: nuclear@3: // Debug method only. nuclear@3: int GetRefCount() const { return RefCount; } nuclear@3: nuclear@3: // This logic is used to detect invalid 'delete' calls of reference counted nuclear@3: // objects. Direct delete calls are not allowed on them unless they come in nuclear@3: // internally from Release. nuclear@3: #ifdef OVR_BUILD_DEBUG nuclear@3: static void OVR_CDECL reportInvalidDelete(void *pmem); nuclear@3: inline static void checkInvalidDelete(RefCountImplCore *pmem) nuclear@3: { nuclear@3: if (pmem->RefCount != 0) nuclear@3: reportInvalidDelete(pmem); nuclear@3: } nuclear@3: #else nuclear@3: inline static void checkInvalidDelete(RefCountImplCore *) { } nuclear@3: #endif nuclear@3: nuclear@3: // Base class ref-count content should not be copied. nuclear@3: void operator = (const RefCountImplCore &) { } nuclear@3: }; nuclear@3: nuclear@3: class RefCountNTSImplCore nuclear@3: { nuclear@3: protected: nuclear@3: mutable int RefCount; nuclear@3: nuclear@3: public: nuclear@3: // RefCountImpl constructor always initializes RefCount to 1 by default. nuclear@3: OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { } nuclear@3: nuclear@3: // Need virtual destructor nuclear@3: // This: 1. Makes sure the right destructor's called. nuclear@3: // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() nuclear@3: virtual ~RefCountNTSImplCore(); nuclear@3: nuclear@3: // Debug method only. nuclear@3: int GetRefCount() const { return RefCount; } nuclear@3: nuclear@3: // This logic is used to detect invalid 'delete' calls of reference counted nuclear@3: // objects. Direct delete calls are not allowed on them unless they come in nuclear@3: // internally from Release. nuclear@3: #ifdef OVR_BUILD_DEBUG nuclear@3: static void OVR_CDECL reportInvalidDelete(void *pmem); nuclear@3: OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem) nuclear@3: { nuclear@3: if (pmem->RefCount != 0) nuclear@3: reportInvalidDelete(pmem); nuclear@3: } nuclear@3: #else nuclear@3: OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { } nuclear@3: #endif nuclear@3: nuclear@3: // Base class ref-count content should not be copied. nuclear@3: void operator = (const RefCountNTSImplCore &) { } nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: nuclear@3: // RefCountImpl provides Thread-Safe implementation of reference counting, so nuclear@3: // it should be used by default in most places. nuclear@3: nuclear@3: class RefCountImpl : public RefCountImplCore nuclear@3: { nuclear@3: public: nuclear@3: // Thread-Safe Ref-Count Implementation. nuclear@3: void AddRef(); nuclear@3: void Release(); nuclear@3: }; nuclear@3: nuclear@3: // RefCountVImpl provides Thread-Safe implementation of reference counting, plus, nuclear@3: // virtual AddRef and Release. nuclear@3: nuclear@3: class RefCountVImpl : public RefCountImplCore nuclear@3: { nuclear@3: public: nuclear@3: // Thread-Safe Ref-Count Implementation. nuclear@3: virtual void AddRef(); nuclear@3: virtual void Release(); nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: // RefCountImplNTS provides Non-Thread-Safe implementation of reference counting, nuclear@3: // which is slightly more efficient since it doesn't use atomics. nuclear@3: nuclear@3: class RefCountNTSImpl : public RefCountNTSImplCore nuclear@3: { nuclear@3: public: nuclear@3: OVR_FORCE_INLINE void AddRef() const { RefCount++; } nuclear@3: void Release() const; nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: nuclear@3: // RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking nuclear@3: // to the reference counting implementation. Base must be one of the RefCountImpl classes. nuclear@3: nuclear@3: template nuclear@3: class RefCountBaseStatImpl : public Base nuclear@3: { nuclear@3: public: nuclear@3: RefCountBaseStatImpl() { } nuclear@3: nuclear@3: // *** Override New and Delete nuclear@3: nuclear@3: // DOM-IGNORE-BEGIN nuclear@3: // Undef new temporarily if it is being redefined nuclear@3: #ifdef OVR_DEFINE_NEW nuclear@3: #undef new nuclear@3: #endif nuclear@3: nuclear@3: #ifdef OVR_BUILD_DEBUG nuclear@3: // Custom check used to detect incorrect calls of 'delete' on ref-counted objects. nuclear@3: #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \ nuclear@3: do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0) nuclear@3: #else nuclear@3: #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) nuclear@3: #endif nuclear@3: nuclear@3: // Redefine all new & delete operators. nuclear@3: OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) nuclear@3: nuclear@3: #ifdef OVR_DEFINE_NEW nuclear@3: #define new OVR_DEFINE_NEW nuclear@3: #endif nuclear@3: // OVR_BUILD_DEFINE_NEW nuclear@3: // DOM-IGNORE-END nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // *** End user RefCountBase<> classes nuclear@3: nuclear@3: nuclear@3: // RefCountBase is a base class for classes that require thread-safe reference nuclear@3: // counting; it also overrides the new and delete operators to use MemoryHeap. nuclear@3: // nuclear@3: // Reference counted objects start out with RefCount value of 1. Further lifetime nuclear@3: // management is done through the AddRef() and Release() methods, typically nuclear@3: // hidden by Ptr<>. nuclear@3: nuclear@3: template nuclear@3: class RefCountBase : public RefCountBaseStatImpl nuclear@3: { nuclear@3: public: nuclear@3: // Constructor. nuclear@3: OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl() { } nuclear@3: }; nuclear@3: nuclear@3: // RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release nuclear@3: nuclear@3: template nuclear@3: class RefCountBaseV : public RefCountBaseStatImpl nuclear@3: { nuclear@3: public: nuclear@3: // Constructor. nuclear@3: OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatImpl() { } nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: // RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference nuclear@3: // counting; it also overrides the new and delete operators to use MemoryHeap. nuclear@3: // This class should only be used if all pointers to it are known to be assigned, nuclear@3: // destroyed and manipulated within one thread. nuclear@3: // nuclear@3: // Reference counted objects start out with RefCount value of 1. Further lifetime nuclear@3: // management is done through the AddRef() and Release() methods, typically nuclear@3: // hidden by Ptr<>. nuclear@3: nuclear@3: template nuclear@3: class RefCountBaseNTS : public RefCountBaseStatImpl nuclear@3: { nuclear@3: public: nuclear@3: // Constructor. nuclear@3: OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl() { } nuclear@3: }; nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Pickable template pointer nuclear@3: enum PickType { PickValue }; nuclear@3: nuclear@3: template nuclear@3: class Pickable nuclear@3: { nuclear@3: public: nuclear@3: Pickable() : pV(NULL) {} nuclear@3: explicit Pickable(T* p) : pV(p) {} nuclear@3: Pickable(T* p, PickType) : pV(p) nuclear@3: { nuclear@3: OVR_ASSERT(pV); nuclear@3: if (pV) nuclear@3: pV->AddRef(); nuclear@3: } nuclear@3: template nuclear@3: Pickable(const Pickable& other) : pV(other.GetPtr()) {} nuclear@3: nuclear@3: public: nuclear@3: Pickable& operator =(const Pickable& other) nuclear@3: { nuclear@3: OVR_ASSERT(pV == NULL); nuclear@3: pV = other.pV; nuclear@3: // Extra check. nuclear@3: //other.pV = NULL; nuclear@3: return *this; nuclear@3: } nuclear@3: nuclear@3: public: nuclear@3: T* GetPtr() const { return pV; } nuclear@3: T* operator->() const nuclear@3: { nuclear@3: return pV; nuclear@3: } nuclear@3: T& operator*() const nuclear@3: { nuclear@3: OVR_ASSERT(pV); nuclear@3: return *pV; nuclear@3: } nuclear@3: nuclear@3: private: nuclear@3: T* pV; nuclear@3: }; nuclear@3: nuclear@3: template nuclear@3: OVR_FORCE_INLINE nuclear@3: Pickable MakePickable(T* p) nuclear@3: { nuclear@3: return Pickable(p); nuclear@3: } nuclear@3: nuclear@3: //----------------------------------------------------------------------------------- nuclear@3: // ***** Ref-Counted template pointer nuclear@3: nuclear@3: // Automatically AddRefs and Releases interfaces nuclear@3: nuclear@3: void* ReturnArg0(void* p); nuclear@3: nuclear@3: template nuclear@3: class Ptr nuclear@3: { nuclear@3: #ifdef OVR_CC_ARM nuclear@3: static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); } nuclear@3: #endif nuclear@3: nuclear@3: protected: nuclear@3: C *pObject; nuclear@3: nuclear@3: public: nuclear@3: nuclear@3: // Constructors nuclear@3: OVR_FORCE_INLINE Ptr() : pObject(0) nuclear@3: { } nuclear@3: #ifdef OVR_CC_ARM nuclear@3: OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj)) nuclear@3: #else nuclear@3: OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj) nuclear@3: #endif nuclear@3: { } nuclear@3: OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) nuclear@3: { nuclear@3: // No AddRef() on purpose. nuclear@3: } nuclear@3: OVR_FORCE_INLINE Ptr(Ptr& other, PickType) : pObject(other.pObject) nuclear@3: { nuclear@3: other.pObject = NULL; nuclear@3: // No AddRef() on purpose. nuclear@3: } nuclear@3: OVR_FORCE_INLINE Ptr(C *pobj) nuclear@3: { nuclear@3: if (pobj) pobj->AddRef(); nuclear@3: pObject = pobj; nuclear@3: } nuclear@3: OVR_FORCE_INLINE Ptr(const Ptr &src) nuclear@3: { nuclear@3: if (src.pObject) src.pObject->AddRef(); nuclear@3: pObject = src.pObject; nuclear@3: } nuclear@3: nuclear@3: template nuclear@3: OVR_FORCE_INLINE Ptr(Ptr &src) nuclear@3: { nuclear@3: if (src) src->AddRef(); nuclear@3: pObject = src; nuclear@3: } nuclear@3: template nuclear@3: OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) nuclear@3: { nuclear@3: // No AddRef() on purpose. nuclear@3: } nuclear@3: nuclear@3: // Destructor nuclear@3: OVR_FORCE_INLINE ~Ptr() nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: } nuclear@3: nuclear@3: // Compares nuclear@3: OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; } nuclear@3: OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; } nuclear@3: nuclear@3: OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; } nuclear@3: OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; } nuclear@3: nuclear@3: nuclear@3: OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; } nuclear@3: nuclear@3: // Assignment nuclear@3: template nuclear@3: OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) nuclear@3: { nuclear@3: if (src) src->AddRef(); nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = src; nuclear@3: return *this; nuclear@3: } nuclear@3: // Specialization nuclear@3: OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) nuclear@3: { nuclear@3: if (src) src->AddRef(); nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = src; nuclear@3: return *this; nuclear@3: } nuclear@3: nuclear@3: OVR_FORCE_INLINE const Ptr& operator = (C *psrc) nuclear@3: { nuclear@3: if (psrc) psrc->AddRef(); nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = psrc; nuclear@3: return *this; nuclear@3: } nuclear@3: OVR_FORCE_INLINE const Ptr& operator = (C &src) nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = &src; nuclear@3: return *this; nuclear@3: } nuclear@3: OVR_FORCE_INLINE Ptr& operator = (Pickable src) nuclear@3: { nuclear@3: return Pick(src); nuclear@3: } nuclear@3: template nuclear@3: OVR_FORCE_INLINE Ptr& operator = (Pickable src) nuclear@3: { nuclear@3: return Pick(src); nuclear@3: } nuclear@3: nuclear@3: // Set Assignment nuclear@3: template nuclear@3: OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) nuclear@3: { nuclear@3: if (src) src->AddRef(); nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = src; nuclear@3: return *this; nuclear@3: } nuclear@3: // Specialization nuclear@3: OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) nuclear@3: { nuclear@3: if (src) src->AddRef(); nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = src; nuclear@3: return *this; nuclear@3: } nuclear@3: nuclear@3: OVR_FORCE_INLINE Ptr& SetPtr(C *psrc) nuclear@3: { nuclear@3: if (psrc) psrc->AddRef(); nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = psrc; nuclear@3: return *this; nuclear@3: } nuclear@3: OVR_FORCE_INLINE Ptr& SetPtr(C &src) nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = &src; nuclear@3: return *this; nuclear@3: } nuclear@3: OVR_FORCE_INLINE Ptr& SetPtr(Pickable src) nuclear@3: { nuclear@3: return Pick(src); nuclear@3: } nuclear@3: nuclear@3: // Nulls ref-counted pointer without decrement nuclear@3: OVR_FORCE_INLINE void NullWithoutRelease() nuclear@3: { nuclear@3: pObject = 0; nuclear@3: } nuclear@3: nuclear@3: // Clears the pointer to the object nuclear@3: OVR_FORCE_INLINE void Clear() nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = 0; nuclear@3: } nuclear@3: nuclear@3: // Obtain pointer reference directly, for D3D interfaces nuclear@3: OVR_FORCE_INLINE C*& GetRawRef() { return pObject; } nuclear@3: nuclear@3: // Access Operators nuclear@3: OVR_FORCE_INLINE C* GetPtr() const { return pObject; } nuclear@3: OVR_FORCE_INLINE C& operator * () const { return *pObject; } nuclear@3: OVR_FORCE_INLINE C* operator -> () const { return pObject; } nuclear@3: // Conversion nuclear@3: OVR_FORCE_INLINE operator C* () const { return pObject; } nuclear@3: nuclear@3: // Pickers. nuclear@3: nuclear@3: // Pick a value. nuclear@3: OVR_FORCE_INLINE Ptr& Pick(Ptr& other) nuclear@3: { nuclear@3: if (&other != this) nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = other.pObject; nuclear@3: other.pObject = 0; nuclear@3: } nuclear@3: nuclear@3: return *this; nuclear@3: } nuclear@3: nuclear@3: OVR_FORCE_INLINE Ptr& Pick(Pickable v) nuclear@3: { nuclear@3: if (v.GetPtr() != pObject) nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = v.GetPtr(); nuclear@3: } nuclear@3: nuclear@3: return *this; nuclear@3: } nuclear@3: nuclear@3: template nuclear@3: OVR_FORCE_INLINE Ptr& Pick(Pickable v) nuclear@3: { nuclear@3: if (v.GetPtr() != pObject) nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = v.GetPtr(); nuclear@3: } nuclear@3: nuclear@3: return *this; nuclear@3: } nuclear@3: nuclear@3: OVR_FORCE_INLINE Ptr& Pick(C* p) nuclear@3: { nuclear@3: if (p != pObject) nuclear@3: { nuclear@3: if (pObject) pObject->Release(); nuclear@3: pObject = p; nuclear@3: } nuclear@3: nuclear@3: return *this; nuclear@3: } nuclear@3: }; nuclear@3: nuclear@3: } // OVR nuclear@3: nuclear@3: #endif