Path: blob/master/thirdparty/jolt_physics/Jolt/Core/Reference.h
9906 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Core/Atomics.h>78JPH_NAMESPACE_BEGIN910// Forward declares11template <class T> class Ref;12template <class T> class RefConst;1314/// Simple class to facilitate reference counting / releasing15/// Derive your class from RefTarget and you can reference it by using Ref<classname> or RefConst<classname>16///17/// Reference counting classes keep an integer which indicates how many references18/// to the object are active. Reference counting objects are derived from RefTarget19/// and staT & their life with a reference count of zero. They can then be assigned20/// to equivalents of pointers (Ref) which will increase the reference count immediately.21/// If the destructor of Ref is called or another object is assigned to the reference22/// counting pointer it will decrease the reference count of the object again. If this23/// reference count becomes zero, the object is destroyed.24///25/// This provides a very powerful mechanism to prevent memory leaks, but also gives26/// some responsibility to the programmer. The most notable point is that you cannot27/// have one object reference another and have the other reference the first one28/// back, because this way the reference count of both objects will never become29/// lower than 1, resulting in a memory leak. By carefully designing your classes30/// (and particularly identifying who owns who in the class hierarchy) you can avoid31/// these problems.32template <class T>33class RefTarget34{35public:36/// Constructor37inline RefTarget() = default;38inline RefTarget(const RefTarget &) { /* Do not copy refcount */ }39inline ~RefTarget() { JPH_IF_ENABLE_ASSERTS(uint32 value = mRefCount.load(memory_order_relaxed);) JPH_ASSERT(value == 0 || value == cEmbedded); } ///< assert no one is referencing us4041/// Mark this class as embedded, this means the type can be used in a compound or constructed on the stack.42/// The Release function will never destruct the object, it is assumed the destructor will be called by whoever allocated43/// the object and at that point in time it is checked that no references are left to the structure.44inline void SetEmbedded() const { JPH_IF_ENABLE_ASSERTS(uint32 old = ) mRefCount.fetch_add(cEmbedded, memory_order_relaxed); JPH_ASSERT(old < cEmbedded); }4546/// Assignment operator47inline RefTarget & operator = (const RefTarget &) { /* Don't copy refcount */ return *this; }4849/// Get current refcount of this object50uint32 GetRefCount() const { return mRefCount.load(memory_order_relaxed); }5152/// Add or release a reference to this object53inline void AddRef() const54{55// Adding a reference can use relaxed memory ordering56mRefCount.fetch_add(1, memory_order_relaxed);57}5859inline void Release() const60{61#ifndef JPH_TSAN_ENABLED62// Releasing a reference must use release semantics...63if (mRefCount.fetch_sub(1, memory_order_release) == 1)64{65// ... so that we can use acquire to ensure that we see any updates from other threads that released a ref before deleting the object66atomic_thread_fence(memory_order_acquire);67delete static_cast<const T *>(this);68}69#else70// But under TSAN, we cannot use atomic_thread_fence, so we use an acq_rel operation unconditionally instead71if (mRefCount.fetch_sub(1, memory_order_acq_rel) == 1)72delete static_cast<const T *>(this);73#endif74}7576/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION77static int sInternalGetRefCountOffset() { return offsetof(T, mRefCount); }7879protected:80static constexpr uint32 cEmbedded = 0x0ebedded; ///< A large value that gets added to the refcount to mark the object as embedded8182mutable atomic<uint32> mRefCount = 0; ///< Current reference count83};8485/// Pure virtual version of RefTarget86class JPH_EXPORT RefTargetVirtual87{88public:89/// Virtual destructor90virtual ~RefTargetVirtual() = default;9192/// Virtual add reference93virtual void AddRef() = 0;9495/// Virtual release reference96virtual void Release() = 0;97};9899/// Class for automatic referencing, this is the equivalent of a pointer to type T100/// if you assign a value to this class it will increment the reference count by one101/// of this object, and if you assign something else it will decrease the reference102/// count of the first object again. If it reaches a reference count of zero it will103/// be deleted104template <class T>105class Ref106{107public:108/// Constructor109inline Ref() : mPtr(nullptr) { }110inline Ref(T *inRHS) : mPtr(inRHS) { AddRef(); }111inline Ref(const Ref<T> &inRHS) : mPtr(inRHS.mPtr) { AddRef(); }112inline Ref(Ref<T> &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }113inline ~Ref() { Release(); }114115/// Assignment operators116inline Ref<T> & operator = (T *inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }117inline Ref<T> & operator = (const Ref<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }118inline Ref<T> & operator = (Ref<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }119120/// Casting operators121inline operator T *() const { return mPtr; }122123/// Access like a normal pointer124inline T * operator -> () const { return mPtr; }125inline T & operator * () const { return *mPtr; }126127/// Comparison128inline bool operator == (const T * inRHS) const { return mPtr == inRHS; }129inline bool operator == (const Ref<T> &inRHS) const { return mPtr == inRHS.mPtr; }130inline bool operator != (const T * inRHS) const { return mPtr != inRHS; }131inline bool operator != (const Ref<T> &inRHS) const { return mPtr != inRHS.mPtr; }132133/// Get pointer134inline T * GetPtr() const { return mPtr; }135136/// Get hash for this object137uint64 GetHash() const138{139return Hash<T *> { } (mPtr);140}141142/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION143void ** InternalGetPointer() { return reinterpret_cast<void **>(&mPtr); }144145private:146template <class T2> friend class RefConst;147148/// Use "variable = nullptr;" to release an object, do not call these functions149inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); }150inline void Release() { if (mPtr != nullptr) mPtr->Release(); }151152T * mPtr; ///< Pointer to object that we are reference counting153};154155/// Class for automatic referencing, this is the equivalent of a CONST pointer to type T156/// if you assign a value to this class it will increment the reference count by one157/// of this object, and if you assign something else it will decrease the reference158/// count of the first object again. If it reaches a reference count of zero it will159/// be deleted160template <class T>161class RefConst162{163public:164/// Constructor165inline RefConst() : mPtr(nullptr) { }166inline RefConst(const T * inRHS) : mPtr(inRHS) { AddRef(); }167inline RefConst(const RefConst<T> &inRHS) : mPtr(inRHS.mPtr) { AddRef(); }168inline RefConst(RefConst<T> &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }169inline RefConst(const Ref<T> &inRHS) : mPtr(inRHS.mPtr) { AddRef(); }170inline RefConst(Ref<T> &&inRHS) noexcept : mPtr(inRHS.mPtr) { inRHS.mPtr = nullptr; }171inline ~RefConst() { Release(); }172173/// Assignment operators174inline RefConst<T> & operator = (const T * inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }175inline RefConst<T> & operator = (const RefConst<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }176inline RefConst<T> & operator = (RefConst<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }177inline RefConst<T> & operator = (const Ref<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }178inline RefConst<T> & operator = (Ref<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }179180/// Casting operators181inline operator const T * () const { return mPtr; }182183/// Access like a normal pointer184inline const T * operator -> () const { return mPtr; }185inline const T & operator * () const { return *mPtr; }186187/// Comparison188inline bool operator == (const T * inRHS) const { return mPtr == inRHS; }189inline bool operator == (const RefConst<T> &inRHS) const { return mPtr == inRHS.mPtr; }190inline bool operator == (const Ref<T> &inRHS) const { return mPtr == inRHS.mPtr; }191inline bool operator != (const T * inRHS) const { return mPtr != inRHS; }192inline bool operator != (const RefConst<T> &inRHS) const { return mPtr != inRHS.mPtr; }193inline bool operator != (const Ref<T> &inRHS) const { return mPtr != inRHS.mPtr; }194195/// Get pointer196inline const T * GetPtr() const { return mPtr; }197198/// Get hash for this object199uint64 GetHash() const200{201return Hash<const T *> { } (mPtr);202}203204/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION205void ** InternalGetPointer() { return const_cast<void **>(reinterpret_cast<const void **>(&mPtr)); }206207private:208/// Use "variable = nullptr;" to release an object, do not call these functions209inline void AddRef() { if (mPtr != nullptr) mPtr->AddRef(); }210inline void Release() { if (mPtr != nullptr) mPtr->Release(); }211212const T * mPtr; ///< Pointer to object that we are reference counting213};214215JPH_NAMESPACE_END216217JPH_SUPPRESS_WARNING_PUSH218JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat")219220namespace std221{222/// Declare std::hash for Ref223template <class T>224struct hash<JPH::Ref<T>>225{226size_t operator () (const JPH::Ref<T> &inRHS) const227{228return size_t(inRHS.GetHash());229}230};231232/// Declare std::hash for RefConst233template <class T>234struct hash<JPH::RefConst<T>>235{236size_t operator () (const JPH::RefConst<T> &inRHS) const237{238return size_t(inRHS.GetHash());239}240};241}242243JPH_SUPPRESS_WARNING_POP244245246