/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* A thread-safe weak pointer */ /** * Derive from SupportsThreadSafeWeakPtr to allow thread-safe weak pointers to * an atomically refcounted derived class. These thread-safe weak pointers may * be safely accessed and converted to strong pointers on multiple threads. * * Note that SupportsThreadSafeWeakPtr defines the same member functions as * AtomicRefCounted, so you should not separately inherit from it. * * ThreadSafeWeakPtr and its implementation is distinct from the normal WeakPtr * which is not thread-safe. The interface discipline and implementation details * are different enough that these two implementations are separated for now for * efficiency reasons. If you don't actually need to use weak pointers on * multiple threads, you can just use WeakPtr instead. * * When deriving from SupportsThreadSafeWeakPtr, you should add * MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public section of your * class, where ClassName is the name of your class. * * Example usage: * * class C : public SupportsThreadSafeWeakPtr * { * public: * MOZ_DECLARE_REFCOUNTED_TYPENAME(C) * void doStuff(); * }; * * ThreadSafeWeakPtr weak; * { * RefPtr strong = new C; * if (strong) { * strong->doStuff(); * } * // Make a new weak reference to the object from the strong reference. * weak = strong; * } * MOZ_ASSERT(!bool(weak), "Weak pointers are cleared after all " * "strong references are released."); * * // Convert the weak reference to a strong reference for usage. * RefPtr other(weak); * if (other) { * other->doStuff(); * } */ #ifndef mozilla_ThreadSafeWeakPtr_h #define mozilla_ThreadSafeWeakPtr_h #include "mozilla/Assertions.h" #include "mozilla/RefCountType.h" #include "mozilla/RefCounted.h" #include "mozilla/RefPtr.h" namespace mozilla { template class ThreadSafeWeakPtr; template class SupportsThreadSafeWeakPtr; namespace detail { class SupportsThreadSafeWeakPtrBase {}; // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr // object. This object owns the reference count for the tracked object, and can // perform atomic refcount upgrades. class ThreadSafeWeakReference : public external::AtomicRefCounted { public: explicit ThreadSafeWeakReference(SupportsThreadSafeWeakPtrBase* aPtr) : mPtr(aPtr) {} #ifdef MOZ_REFCOUNTED_LEAK_CHECKING const char* typeName() const { return "ThreadSafeWeakReference"; } size_t typeSize() const { return sizeof(*this); } #endif private: template friend class mozilla::SupportsThreadSafeWeakPtr; template friend class mozilla::ThreadSafeWeakPtr; // Number of strong references to the underlying data structure. // // Other than the initial strong `AddRef` call incrementing this value to 1, // which must occur before any weak references are taken, once this value // reaches `0` again it cannot be changed. RC mStrongCnt{0}; // Raw pointer to the tracked object. It is never valid to read this value // outside of `ThreadSafeWeakPtr::getRefPtr()`. SupportsThreadSafeWeakPtrBase* MOZ_NON_OWNING_REF mPtr; }; } // namespace detail // For usage documentation for SupportsThreadSafeWeakPtr, see the header-level // documentation. // // To understand the layout of SupportsThreadSafeWeakPtr, consider the following // simplified declaration: // // class MyType: SupportsThreadSafeWeakPtr { uint32_t mMyData; ... } // // Which will result in the following layout: // // +--------------------+ // | MyType | <===============================================+ // +--------------------+ I // | RefPtr mWeakRef o======> +-------------------------------------+ I // | uint32_t mMyData | | ThreadSafeWeakReference | I // +--------------------+ +-------------------------------------+ I // | RC mRefCount | I // | RC mStrongCount | I // | SupportsThreadSafeWeakPtrBase* mPtr o====+ // +-------------------------------------+ // // The mRefCount inherited from AtomicRefCounted is the // weak count. This means MyType implicitly holds a weak reference, so if the // weak count ever hits 0, we know all strong *and* weak references are gone, // and it's safe to free the ThreadSafeWeakReference. MyType's AddRef and // Release implementations otherwise only manipulate mStrongCount. // // It's necessary to keep the counts in a separate allocation because we need // to be able to delete MyType while weak references still exist. This ensures // that weak references can still access all the state necessary to check if // they can be upgraded (mStrongCount). template class SupportsThreadSafeWeakPtr : public detail::SupportsThreadSafeWeakPtrBase { protected: using ThreadSafeWeakReference = detail::ThreadSafeWeakReference; // The `this` pointer will not have subclasses initialized yet, but it will // also not be read until a weak pointer is upgraded, which should be after // this point. SupportsThreadSafeWeakPtr() : mWeakRef(new ThreadSafeWeakReference(this)) { static_assert(std::is_base_of_v, "T must derive from SupportsThreadSafeWeakPtr"); } public: // Compatibility with RefPtr MozExternalRefCountType AddRef() const { auto& refCnt = mWeakRef->mStrongCnt; MOZ_ASSERT(int32_t(refCnt) >= 0); MozRefCountType cnt = ++refCnt; detail::RefCountLogger::logAddRef(static_cast(this), cnt); return cnt; } MozExternalRefCountType Release() const { auto& refCnt = mWeakRef->mStrongCnt; MOZ_ASSERT(int32_t(refCnt) > 0); detail::RefCountLogger::ReleaseLogger logger(static_cast(this)); MozRefCountType cnt = --refCnt; logger.logRelease(cnt); if (0 == cnt) { // Because we have atomically decremented the refcount above, only one // thread can get a 0 count here. Thus, it is safe to access and destroy // |this| here. // No other thread can acquire a strong reference to |this| anymore // through our weak pointer, as upgrading a weak pointer always uses // |IncrementIfNonzero|, meaning the refcount can't leave a zero reference // state. // NOTE: We can't update our refcount to the marker `DEAD` value here, as // it may still be read by mWeakRef. delete static_cast(this); } return cnt; } using HasThreadSafeRefCnt = std::true_type; // Compatibility with wtf::RefPtr void ref() { AddRef(); } void deref() { Release(); } MozRefCountType refCount() const { return mWeakRef->mStrongCnt; } bool hasOneRef() const { return refCount() == 1; } private: template friend class ThreadSafeWeakPtr; ThreadSafeWeakReference* getThreadSafeWeakReference() const { return mWeakRef; } const RefPtr mWeakRef; }; // A thread-safe variant of a weak pointer template class ThreadSafeWeakPtr { using ThreadSafeWeakReference = detail::ThreadSafeWeakReference; public: ThreadSafeWeakPtr() = default; ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) = default; ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) = default; ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) = default; ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) = default; ThreadSafeWeakPtr& operator=(const RefPtr& aOther) { if (aOther) { // Get the underlying shared weak reference to the object. mRef = aOther->getThreadSafeWeakReference(); } else { mRef = nullptr; } return *this; } explicit ThreadSafeWeakPtr(const RefPtr& aOther) { *this = aOther; } ThreadSafeWeakPtr& operator=(decltype(nullptr)) { mRef = nullptr; return *this; } explicit ThreadSafeWeakPtr(decltype(nullptr)) {} // Use the explicit `IsNull()` or `IsDead()` methods instead. explicit operator bool() const = delete; // Check if the ThreadSafeWeakPtr was created wrapping a null pointer. bool IsNull() const { return !mRef; } // Check if the managed object is nullptr or has already been destroyed. Once // IsDead returns true, this ThreadSafeWeakPtr can never be upgraded again // (until it has been re-assigned), but a false return value does NOT imply // that any future upgrade will be successful. bool IsDead() const { return IsNull() || size_t(mRef->mStrongCnt) == 0; } bool operator==(const ThreadSafeWeakPtr& aOther) const { return mRef == aOther.mRef; } bool operator==(const RefPtr& aOther) const { return *this == aOther.get(); } friend bool operator==(const RefPtr& aStrong, const ThreadSafeWeakPtr& aWeak) { return aWeak == aStrong.get(); } bool operator==(const T* aOther) const { if (!mRef) { return !aOther; } return aOther && aOther->getThreadSafeWeakReference() == mRef; } template bool operator!=(const U& aOther) const { return !(*this == aOther); } // Convert the weak pointer to a strong RefPtr. explicit operator RefPtr() const { return getRefPtr(); } private: // Gets a new strong reference of the proper type T to the tracked object. already_AddRefed getRefPtr() const { if (!mRef) { return nullptr; } // Increment our strong reference count only if it is nonzero, meaning that // the object is still alive. MozRefCountType cnt = mRef->mStrongCnt.IncrementIfNonzero(); if (cnt == 0) { return nullptr; } RefPtr ptr = already_AddRefed(static_cast(mRef->mPtr)); detail::RefCountLogger::logAddRef(ptr.get(), cnt); return ptr.forget(); } // A shared weak reference to an object. Note that this may be null so as to // save memory (at the slight cost of an extra null check) if no object is // being tracked. RefPtr mRef; }; } // namespace mozilla template inline already_AddRefed do_AddRef( const mozilla::ThreadSafeWeakPtr& aObj) { RefPtr ref(aObj); return ref.forget(); } #endif /* mozilla_ThreadSafeWeakPtr_h */