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