Reference.h 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. JPH_SUPPRESS_WARNINGS_STD_BEGIN
  5. #include <atomic>
  6. JPH_SUPPRESS_WARNINGS_STD_END
  7. JPH_NAMESPACE_BEGIN
  8. // Forward declares
  9. template <class T> class Ref;
  10. template <class T> class RefConst;
  11. /// Simple class to facilitate reference counting / releasing
  12. /// Derive your class from RefTarget and you can reference it by using Ref<classname> or RefConst<classname>
  13. ///
  14. /// Reference counting classes keep an integer which indicates how many references
  15. /// to the object are active. Reference counting objects are derived from RefTarget
  16. /// and staT & their life with a reference count of zero. They can then be assigned
  17. /// to equivalents of pointers (Ref) which will increase the reference count immediately.
  18. /// If the destructor of Ref is called or another object is assigned to the reference
  19. /// counting pointer it will decrease the reference count of the object again. If this
  20. /// reference count becomes zero, the object is destroyed.
  21. ///
  22. /// This provides a very powerful mechanism to prevent memory leaks, but also gives
  23. /// some responsibility to the programmer. The most notable point is that you cannot
  24. /// have one object reference another and have the other reference the first one
  25. /// back, because this way the reference count of both objects will never become
  26. /// lower than 1, resulting in a memory leak. By carefully designing your classses
  27. /// (and particularly identifying who owns who in the class hierarchy) you can avoid
  28. /// these problems.
  29. template <class T>
  30. class RefTarget
  31. {
  32. public:
  33. /// Constructor
  34. inline RefTarget() = default;
  35. inline RefTarget(const RefTarget &) { /* Do not copy refcount */ }
  36. inline ~RefTarget() { JPH_IF_ENABLE_ASSERTS(uint32 value = mRefCount.load(memory_order_relaxed);) JPH_ASSERT(value == 0 || value == cEmbedded); } ///< assert no one is referencing us
  37. /// Mark this class as embedded, this means the type can be used in a compound or constructed on the stack.
  38. /// The Release function will never destruct the object, it is assumed the destructor will be called by whoever allocated
  39. /// the object and at that point in time it is checked that no references are left to the structure.
  40. inline void SetEmbedded() const { JPH_IF_ENABLE_ASSERTS(uint32 old = ) mRefCount.fetch_add(cEmbedded, memory_order_relaxed); JPH_ASSERT(old < cEmbedded); }
  41. /// Assignment operator
  42. inline RefTarget & operator = (const RefTarget &) { /* Don't copy refcount */ return *this; }
  43. /// Get current refcount of this object
  44. uint32 GetRefCount() const { return mRefCount.load(memory_order_relaxed); }
  45. /// Add or release a reference to this object
  46. inline void AddRef() const
  47. {
  48. // Adding a reference can use relaxed memory ordering
  49. mRefCount.fetch_add(1, memory_order_relaxed);
  50. }
  51. inline void Release() const
  52. {
  53. // Releasing a reference must use release semantics...
  54. if (mRefCount.fetch_sub(1, memory_order_release) == 1)
  55. {
  56. // ... so that we can use aquire to ensure that we see any updates from other threads that released a ref before deleting the object
  57. atomic_thread_fence(memory_order_acquire);
  58. delete static_cast<const T *>(this);
  59. }
  60. }
  61. /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
  62. static int sInternalGetRefCountOffset() { return offsetof(T, mRefCount); }
  63. protected:
  64. static constexpr uint32 cEmbedded = 0x0ebedded; ///< A large value that gets added to the refcount to mark the object as embedded
  65. mutable atomic<uint32> mRefCount = 0; ///< Current reference count
  66. };
  67. /// Pure virtual version of RefTarget
  68. class RefTargetVirtual
  69. {
  70. public:
  71. /// Virtual destructor
  72. virtual ~RefTargetVirtual() = default;
  73. /// Virtual add reference
  74. virtual void AddRef() = 0;
  75. /// Virtual release reference
  76. virtual void Release() = 0;
  77. };
  78. /// Class for automatic referencing, this is the equivalent of a pointer to type T
  79. /// if you assign a value to this class it will increment the reference count by one
  80. /// of this object, and if you assign something else it will decrease the reference
  81. /// count of the first object again. If it reaches a reference count of zero it will
  82. /// be deleted
  83. template <class T>
  84. class Ref
  85. {
  86. public:
  87. /// Constructor
  88. inline Ref() : mPtr(nullptr) { }
  89. inline Ref(T *inRHS) : mPtr(inRHS) { AddRef(); }
  90. inline Ref(const Ref<T> &inRHS) : mPtr(inRHS.mPtr) { AddRef(); }
  91. inline Ref(Ref<T> &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }
  92. inline ~Ref() { Release(); }
  93. /// Assignment operators
  94. inline Ref<T> & operator = (T *inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
  95. inline Ref<T> & operator = (const Ref<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
  96. inline Ref<T> & operator = (Ref<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
  97. /// Casting operators
  98. inline operator T * const () const { return mPtr; }
  99. inline operator T *() { return mPtr; }
  100. /// Access like a normal pointer
  101. inline T * const operator -> () const { return mPtr; }
  102. inline T * operator -> () { return mPtr; }
  103. inline T & operator * () const { return *mPtr; }
  104. /// Comparison
  105. inline bool operator == (const T * inRHS) const { return mPtr == inRHS; }
  106. inline bool operator == (const Ref<T> &inRHS) const { return mPtr == inRHS.mPtr; }
  107. inline bool operator != (const T * inRHS) const { return mPtr != inRHS; }
  108. inline bool operator != (const Ref<T> &inRHS) const { return mPtr != inRHS.mPtr; }
  109. /// Get pointer
  110. inline T * GetPtr() const { return mPtr; }
  111. inline T * GetPtr() { return mPtr; }
  112. /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
  113. void ** InternalGetPointer() { return reinterpret_cast<void **>(&mPtr); }
  114. private:
  115. template <class T2> friend class RefConst;
  116. /// Use "variable = nullptr;" to release an object, do not call these functions
  117. inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); }
  118. inline void Release() { if (mPtr != nullptr) mPtr->Release(); }
  119. T * mPtr; ///< Pointer to object that we are reference counting
  120. };
  121. /// Class for automatic referencing, this is the equivalent of a CONST pointer to type T
  122. /// if you assign a value to this class it will increment the reference count by one
  123. /// of this object, and if you assign something else it will decrease the reference
  124. /// count of the first object again. If it reaches a reference count of zero it will
  125. /// be deleted
  126. template <class T>
  127. class RefConst
  128. {
  129. public:
  130. /// Constructor
  131. inline RefConst() : mPtr(nullptr) { }
  132. inline RefConst(const T * inRHS) : mPtr(inRHS) { AddRef(); }
  133. inline RefConst(const RefConst<T> &inRHS) : mPtr(inRHS.mPtr) { AddRef(); }
  134. inline RefConst(RefConst<T> &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }
  135. inline RefConst(const Ref<T> &inRHS) : mPtr(inRHS.mPtr) { AddRef(); }
  136. inline RefConst(Ref<T> &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }
  137. inline ~RefConst() { Release(); }
  138. /// Assignment operators
  139. inline RefConst<T> & operator = (const T * inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
  140. inline RefConst<T> & operator = (const RefConst<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
  141. inline RefConst<T> & operator = (RefConst<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
  142. inline RefConst<T> & operator = (const Ref<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
  143. inline RefConst<T> & operator = (Ref<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
  144. /// Casting operators
  145. inline operator const T * () const { return mPtr; }
  146. /// Access like a normal pointer
  147. inline const T * operator -> () const { return mPtr; }
  148. inline const T & operator * () const { return *mPtr; }
  149. /// Comparison
  150. inline bool operator == (const T * inRHS) const { return mPtr == inRHS; }
  151. inline bool operator == (const RefConst<T> &inRHS) const { return mPtr == inRHS.mPtr; }
  152. inline bool operator == (const Ref<T> &inRHS) const { return mPtr == inRHS.mPtr; }
  153. inline bool operator != (const T * inRHS) const { return mPtr != inRHS; }
  154. inline bool operator != (const RefConst<T> &inRHS) const { return mPtr != inRHS.mPtr; }
  155. inline bool operator != (const Ref<T> &inRHS) const { return mPtr != inRHS.mPtr; }
  156. /// Get pointer
  157. inline const T * GetPtr() const { return mPtr; }
  158. /// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
  159. void ** InternalGetPointer() { return const_cast<void **>(reinterpret_cast<const void **>(&mPtr)); }
  160. private:
  161. /// Use "variable = nullptr;" to release an object, do not call these functions
  162. inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); }
  163. inline void Release() { if (mPtr != nullptr) mPtr->Release(); }
  164. const T * mPtr; ///< Pointer to object that we are reference counting
  165. };
  166. JPH_NAMESPACE_END
  167. JPH_SUPPRESS_WARNING_PUSH
  168. JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
  169. namespace std
  170. {
  171. /// Declare std::hash for Ref
  172. template <class T>
  173. struct hash<JPH::Ref<T>>
  174. {
  175. size_t operator () (const JPH::Ref<T> &inRHS) const
  176. {
  177. return hash<T *> { }(inRHS.GetPtr());
  178. }
  179. };
  180. /// Declare std::hash for RefConst
  181. template <class T>
  182. struct hash<JPH::RefConst<T>>
  183. {
  184. size_t operator () (const JPH::RefConst<T> &inRHS) const
  185. {
  186. return hash<const T *> { }(inRHS.GetPtr());
  187. }
  188. };
  189. }
  190. JPH_SUPPRESS_WARNING_POP