/* * Copyright 2019-2021 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * In no event and under no legal theory, whether in tort (including negligence), * contract, or otherwise, unless required by applicable law (such as deliberate * and grossly negligent acts) or agreed to in writing, shall any Contributor be * liable for any damages, including any direct, indirect, special, incidental, * or consequential damages of any character arising as a result of this License or * out of the use or inability to use the software (including but not limited to damages * for loss of goodwill, work stoppage, computer failure or malfunction, or any and * all other commercial damages or losses), even if such Contributor has been advised * of the possibility of such damages. */ #pragma once #include "../../Primitives/interface/Object.h" #include "../../Platforms/interface/Atomics.hpp" #include "ValidatedCast.hpp" #include "RefCountedObjectImpl.hpp" namespace Diligent { template class RefCntWeakPtr; // The main advantage of RefCntAutoPtr over the std::shared_ptr is that you can // attach the same raw pointer to different smart pointers. // // For instance, the following code will crash since p will be released twice: // // auto *p = new char; // std::shared_ptr pTmp1(p); // std::shared_ptr pTmp2(p); // ... // This code, in contrast, works perfectly fine: // // ObjectBase *pRawPtr(new ObjectBase); // RefCntAutoPtr pSmartPtr1(pRawPtr); // RefCntAutoPtr pSmartPtr2(pRawPtr); // ... // Other advantage is that weak pointers remain valid until the // object is alive, even if all smart pointers were destroyed: // // RefCntWeakPtr pWeakPtr(pSmartPtr1); // pSmartPtr1.Release(); // auto pSmartPtr3 = pWeakPtr.Lock(); // .. // Weak pointers can also be attached directly to the object: // RefCntWeakPtr pWeakPtr(pRawPtr); // /// Template class that implements reference counting template class RefCntAutoPtr { public: RefCntAutoPtr() noexcept {} explicit RefCntAutoPtr(T* pObj) noexcept : m_pObject{pObj} { if (m_pObject) m_pObject->AddRef(); } RefCntAutoPtr(IObject* pObj, const INTERFACE_ID& IID) noexcept : m_pObject{nullptr} { if (pObj) pObj->QueryInterface(IID, reinterpret_cast(&m_pObject)); } // Copy constructor must not be template! RefCntAutoPtr(const RefCntAutoPtr& AutoPtr) noexcept : m_pObject{AutoPtr.m_pObject} { if (m_pObject) m_pObject->AddRef(); } template ::value>::type> RefCntAutoPtr(const RefCntAutoPtr& AutoPtr) noexcept : RefCntAutoPtr{AutoPtr.m_pObject} { } // Non-template move constructor RefCntAutoPtr(RefCntAutoPtr&& AutoPtr) noexcept : m_pObject{std::move(AutoPtr.m_pObject)} { //Make sure original pointer has no references to the object AutoPtr.m_pObject = nullptr; } template ::value>::type> RefCntAutoPtr(RefCntAutoPtr&& AutoPtr) noexcept : m_pObject{std::move(AutoPtr.m_pObject)} { //Make sure original pointer has no references to the object AutoPtr.m_pObject = nullptr; } ~RefCntAutoPtr() { Release(); } void swap(RefCntAutoPtr& AutoPtr) noexcept { std::swap(m_pObject, AutoPtr.m_pObject); } void Attach(T* pObj) noexcept { Release(); m_pObject = pObj; } T* Detach() noexcept { T* pObj = m_pObject; m_pObject = nullptr; return pObj; } void Release() noexcept { if (m_pObject) { m_pObject->Release(); m_pObject = nullptr; } } RefCntAutoPtr& operator=(T* pObj) noexcept { if (m_pObject != pObj) { if (m_pObject) m_pObject->Release(); m_pObject = pObj; if (m_pObject) m_pObject->AddRef(); } return *this; } RefCntAutoPtr& operator=(const RefCntAutoPtr& AutoPtr) noexcept { return *this = AutoPtr.m_pObject; } template ::value>::type> RefCntAutoPtr& operator=(const RefCntAutoPtr& AutoPtr) noexcept { return *this = static_cast(AutoPtr.m_pObject); } RefCntAutoPtr& operator=(RefCntAutoPtr&& AutoPtr) noexcept { if (m_pObject != AutoPtr.m_pObject) Attach(AutoPtr.Detach()); return *this; } template ::value>::type> RefCntAutoPtr& operator=(RefCntAutoPtr&& AutoPtr) noexcept { if (m_pObject != AutoPtr.m_pObject) Attach(AutoPtr.Detach()); return *this; } // All the access functions do not require locking reference counters pointer because if it is valid, // the smart pointer holds strong reference to the object and it thus cannot be released by // ohter thread bool operator!() const noexcept { return m_pObject == nullptr; } explicit operator bool() const noexcept { return m_pObject != nullptr; } bool operator==(const RefCntAutoPtr& Ptr) const noexcept { return m_pObject == Ptr.m_pObject; } bool operator!=(const RefCntAutoPtr& Ptr) const noexcept { return m_pObject != Ptr.m_pObject; } bool operator<(const RefCntAutoPtr& Ptr) const noexcept { return static_cast(*this) < static_cast(Ptr); } T& operator*() noexcept { return *m_pObject; } const T& operator*() const noexcept { return *m_pObject; } T* RawPtr() noexcept { return m_pObject; } const T* RawPtr() const noexcept { return m_pObject; } template DstType* RawPtr() noexcept { return ValidatedCast(m_pObject); } template DstType* RawPtr() const noexcept { return ValidatedCast(m_pObject); } operator T*() noexcept { return RawPtr(); } operator const T*() const noexcept { return RawPtr(); } T* operator->() noexcept { return m_pObject; } const T* operator->() const noexcept { return m_pObject; } template RefCntAutoPtr Cast(const INTERFACE_ID& IID) const { return RefCntAutoPtr{m_pObject, IID}; } private: // Note that the DoublePtrHelper is a private class, and can be created only by RefCntWeakPtr // Thus if no special effort is made, the lifetime of the instances of this class cannot be // longer than the lifetime of the creating object template class DoublePtrHelper { public: DoublePtrHelper(RefCntAutoPtr& AutoPtr) noexcept : NewRawPtr{static_cast(AutoPtr)}, m_pAutoPtr{std::addressof(AutoPtr)} { } DoublePtrHelper(DoublePtrHelper&& Helper) noexcept : NewRawPtr{Helper.NewRawPtr}, m_pAutoPtr{Helper.m_pAutoPtr} { Helper.m_pAutoPtr = nullptr; Helper.NewRawPtr = nullptr; } ~DoublePtrHelper() { if (m_pAutoPtr && *m_pAutoPtr != static_cast(NewRawPtr)) { m_pAutoPtr->Attach(static_cast(NewRawPtr)); } } DstType*& operator*() noexcept { return NewRawPtr; } const DstType* operator*() const noexcept { return NewRawPtr; } // clang-format off operator DstType**() noexcept { return &NewRawPtr; } operator const DstType**() const noexcept { return &NewRawPtr; } // clang-format on private: DstType* NewRawPtr; RefCntAutoPtr* m_pAutoPtr; // clang-format off DoublePtrHelper (const DoublePtrHelper&) = delete; DoublePtrHelper& operator = (const DoublePtrHelper&) = delete; DoublePtrHelper& operator = (DoublePtrHelper&&) = delete; // clang-format on }; public: template ::value>::type> DoublePtrHelper DblPtr() noexcept { return DoublePtrHelper(*this); } template ::value>::type> DoublePtrHelper DblPtr() const noexcept { return DoublePtrHelper(*this); } DoublePtrHelper operator&() { return DblPtr(); } const DoublePtrHelper operator&() const { return DblPtr(); } T** RawDblPtr() noexcept { return &m_pObject; } const T** RawDblPtr() const noexcept { return &m_pObject; } template ::value>::type> DstType** RawDblPtr() noexcept { return reinterpret_cast(&m_pObject); } template ::value>::type> DstType** RawDblPtr() const noexcept { return reinterpret_cast(&m_pObject); } private: template friend class RefCntAutoPtr; T* m_pObject = nullptr; }; /// Implementation of weak pointers template class RefCntWeakPtr { public: explicit RefCntWeakPtr(T* pObj = nullptr) noexcept : m_pRefCounters{nullptr}, m_pObject{pObj} { if (m_pObject) { m_pRefCounters = ValidatedCast(m_pObject->GetReferenceCounters()); m_pRefCounters->AddWeakRef(); } } ~RefCntWeakPtr() { Release(); } RefCntWeakPtr(const RefCntWeakPtr& WeakPtr) noexcept : m_pRefCounters{WeakPtr.m_pRefCounters}, m_pObject{WeakPtr.m_pObject} { if (m_pRefCounters) m_pRefCounters->AddWeakRef(); } RefCntWeakPtr(RefCntWeakPtr&& WeakPtr) noexcept : m_pRefCounters{std::move(WeakPtr.m_pRefCounters)}, m_pObject{std::move(WeakPtr.m_pObject)} { WeakPtr.m_pRefCounters = nullptr; WeakPtr.m_pObject = nullptr; } explicit RefCntWeakPtr(RefCntAutoPtr& AutoPtr) noexcept : m_pRefCounters{AutoPtr ? ValidatedCast(AutoPtr->GetReferenceCounters()) : nullptr}, m_pObject{static_cast(AutoPtr)} { if (m_pRefCounters) m_pRefCounters->AddWeakRef(); } RefCntWeakPtr& operator=(const RefCntWeakPtr& WeakPtr) noexcept { if (*this == WeakPtr) return *this; Release(); m_pObject = WeakPtr.m_pObject; m_pRefCounters = WeakPtr.m_pRefCounters; if (m_pRefCounters) m_pRefCounters->AddWeakRef(); return *this; } RefCntWeakPtr& operator=(T* pObj) noexcept { return operator=(RefCntWeakPtr(pObj)); } RefCntWeakPtr& operator=(RefCntWeakPtr&& WeakPtr) noexcept { if (*this == WeakPtr) return *this; Release(); m_pObject = std::move(WeakPtr.m_pObject); m_pRefCounters = std::move(WeakPtr.m_pRefCounters); WeakPtr.m_pRefCounters = nullptr; WeakPtr.m_pObject = nullptr; return *this; } RefCntWeakPtr& operator=(RefCntAutoPtr& AutoPtr) noexcept { Release(); m_pObject = static_cast(AutoPtr); m_pRefCounters = m_pObject ? ValidatedCast(m_pObject->GetReferenceCounters()) : nullptr; if (m_pRefCounters) m_pRefCounters->AddWeakRef(); return *this; } void Release() noexcept { if (m_pRefCounters) m_pRefCounters->ReleaseWeakRef(); m_pRefCounters = nullptr; m_pObject = nullptr; } /// \note This method may not be reliable in a multithreaded environment. /// However, when false is returned, the strong pointer created from /// this weak pointer will reliably be empty. bool IsValid() const noexcept { return m_pObject != nullptr && m_pRefCounters != nullptr && m_pRefCounters->GetNumStrongRefs() > 0; } /// Obtains a strong reference to the object RefCntAutoPtr Lock() { RefCntAutoPtr spObj; if (m_pRefCounters) { // Try to obtain pointer to the owner object. // spOwner is only used to keep the object // alive while obtaining strong reference from // the raw pointer m_pObject RefCntAutoPtr spOwner; m_pRefCounters->GetObject(&spOwner); if (spOwner) { // If owner is alive, we can use our RAW pointer to // create strong reference spObj = m_pObject; } else { // Owner object has been destroyed. There is no reason // to keep this weak reference anymore Release(); } } return spObj; } bool operator==(const RefCntWeakPtr& Ptr) const noexcept { return m_pRefCounters == Ptr.m_pRefCounters; } bool operator!=(const RefCntWeakPtr& Ptr) const noexcept { return m_pRefCounters != Ptr.m_pRefCounters; } protected: RefCountersImpl* m_pRefCounters; // We need to store raw pointer to object itself, // because if the object is owned by another object, // m_pRefCounters->GetObject( &pObj ) will return // pointer to owner, which is not what we need. T* m_pObject; }; } // namespace Diligent