diff options
Diffstat (limited to 'dom/indexedDB/SafeRefPtr.h')
-rw-r--r-- | dom/indexedDB/SafeRefPtr.h | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/dom/indexedDB/SafeRefPtr.h b/dom/indexedDB/SafeRefPtr.h new file mode 100644 index 0000000000..91fa4dded1 --- /dev/null +++ b/dom/indexedDB/SafeRefPtr.h @@ -0,0 +1,480 @@ +/* -*- 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/. */ + +#ifndef mozilla_saferefptr_h__ +#define mozilla_saferefptr_h__ + +#include "mozilla/ArrayAlgorithm.h" +#include "mozilla/Maybe.h" +#include "mozilla/NotNull.h" +#include "mozilla/RefCounted.h" +#include "mozilla/RefPtr.h" +#include "nsCOMPtr.h" +#include "nsTObserverArray.h" + +namespace mozilla { +template <typename T> +class SafeRefPtr; + +template <typename T, typename... Args> +SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs); + +namespace detail { +struct InitialConstructionTag {}; + +class SafeRefCountedBase { + template <typename U, typename... Args> + friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs); + + template <typename T> + friend class SafeRefPtr; + + void* operator new(size_t aSize) { return ::operator new(aSize); } + + protected: + void operator delete(void* aPtr) { ::operator delete(aPtr); } + + public: + void* operator new[](size_t) = delete; +}; + +// SafeRefCounted is similar to RefCounted, but they differ in their initial +// refcount (here 1), and the visibility of operator new (here private). The +// rest is mostly a copy of RefCounted. +template <typename T, RefCountAtomicity Atomicity> +class SafeRefCounted : public SafeRefCountedBase { + protected: + SafeRefCounted() = default; +#ifdef DEBUG + ~SafeRefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); } +#endif + + public: + // Compatibility with nsRefPtr. + MozRefCountType AddRef() const { + // Note: this method must be thread safe for AtomicRefCounted. + MOZ_ASSERT(int32_t(mRefCnt) >= 0); + const MozRefCountType cnt = ++mRefCnt; + detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt); + return cnt; + } + + MozRefCountType Release() const { + // Note: this method must be thread safe for AtomicRefCounted. + MOZ_ASSERT(int32_t(mRefCnt) > 0); + detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this)); + const MozRefCountType cnt = --mRefCnt; + // Note: it's not safe to touch |this| after decrementing the refcount, + // except for below. + logger.logRelease(cnt); + if (0 == cnt) { + // Because we have atomically decremented the refcount above, only + // one thread can get a 0 count here, so as long as we can assume that + // everything else in the system is accessing this object through + // RefPtrs, it's safe to access |this| here. +#ifdef DEBUG + mRefCnt = detail::DEAD; +#endif + delete static_cast<const T*>(this); + } + return cnt; + } + + // Compatibility with wtf::RefPtr. + void ref() { AddRef(); } + void deref() { Release(); } + MozRefCountType refCount() const { return mRefCnt; } + bool hasOneRef() const { + MOZ_ASSERT(mRefCnt > 0); + return mRefCnt == 1; + } + + protected: + SafeRefPtr<T> SafeRefPtrFromThis(); + + private: + mutable RC<MozRefCountType, Atomicity> mRefCnt = + RC<MozRefCountType, Atomicity>{1}; +}; +} // namespace detail + +template <typename T> +class SafeRefCounted + : public detail::SafeRefCounted<T, detail::NonAtomicRefCount> { + public: + ~SafeRefCounted() { + static_assert(std::is_base_of<SafeRefCounted, T>::value, + "T must derive from SafeRefCounted<T>"); + } +}; + +template <typename T> +class AtomicSafeRefCounted + : public detail::SafeRefCounted<T, detail::AtomicRefCount> { + public: + ~AtomicSafeRefCounted() { + static_assert(std::is_base_of<AtomicSafeRefCounted, T>::value, + "T must derive from AtomicSafeRefCounted<T>"); + } +}; + +struct AcquireStrongRefFromRawPtr {}; + +// XXX for Apple, clang::trivial_abi is probably also supported, but we need to +// find out the correct version number +#if defined(__clang__) && !defined(__apple_build_version__) && \ + __clang_major__ >= 7 +# define MOZ_TRIVIAL_ABI [[clang::trivial_abi]] +#else +# define MOZ_TRIVIAL_ABI +#endif + +// A restricted variant of mozilla::RefPtr<T>, which prohibits some unsafe or +// unperformant misuses, in particular: +// * It is not implicitly convertible from a raw pointer. Unsafe acquisitions +// from a raw pointer must be made using the verbose +// AcquireStrongRefFromRawPtr. To create a new object on the heap, use +// MakeSafeRefPtr. +// * It does not implicitly decay to a raw pointer. unsafeGetRawPtr() must be +// called +// explicitly. +// * It is not copyable, but must be explicitly copied using clonePtr(). +// * Temporaries cannot be dereferenced using operator* or operator->. +template <typename T> +class MOZ_IS_REFPTR MOZ_TRIVIAL_ABI SafeRefPtr { + template <typename U> + friend class SafeRefPtr; + + template <typename U, typename... Args> + friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs); + + T* MOZ_OWNING_REF mRawPtr = nullptr; + + // BEGIN Some things copied from RefPtr. + // We cannot simply use a RefPtr member because we want to be trivial_abi, + // which RefPtr is not. + void assign_with_AddRef(T* aRawPtr) { + if (aRawPtr) { + ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr); + } + assign_assuming_AddRef(aRawPtr); + } + + void assign_assuming_AddRef(T* aNewPtr) { + T* oldPtr = mRawPtr; + mRawPtr = aNewPtr; + if (oldPtr) { + ConstRemovingRefPtrTraits<T>::Release(oldPtr); + } + } + + template <class U> + struct ConstRemovingRefPtrTraits { + static void AddRef(U* aPtr) { mozilla::RefPtrTraits<U>::AddRef(aPtr); } + static void Release(U* aPtr) { mozilla::RefPtrTraits<U>::Release(aPtr); } + }; + template <class U> + struct ConstRemovingRefPtrTraits<const U> { + static void AddRef(const U* aPtr) { + mozilla::RefPtrTraits<U>::AddRef(const_cast<U*>(aPtr)); + } + static void Release(const U* aPtr) { + mozilla::RefPtrTraits<U>::Release(const_cast<U*>(aPtr)); + } + }; + // END Some things copied from RefPtr. + + SafeRefPtr(T* aRawPtr, mozilla::detail::InitialConstructionTag); + + public: + SafeRefPtr() = default; + + template <typename U, + typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> + MOZ_IMPLICIT SafeRefPtr(SafeRefPtr<U>&& aSrc) : mRawPtr(aSrc.mRawPtr) { + aSrc.mRawPtr = nullptr; + } + + explicit SafeRefPtr(RefPtr<T>&& aRefPtr) : mRawPtr(aRefPtr.forget().take()) {} + + // To prevent implicit conversion of raw pointer to RefPtr and then + // calling the previous overload. + SafeRefPtr(T* const aRawPtr) = delete; + + SafeRefPtr(T* const aRawPtr, const AcquireStrongRefFromRawPtr&) { + assign_with_AddRef(aRawPtr); + } + + MOZ_IMPLICIT SafeRefPtr(std::nullptr_t) {} + + // Prevent implicit copying, use clonePtr() instead. + SafeRefPtr(const SafeRefPtr&) = delete; + SafeRefPtr& operator=(const SafeRefPtr&) = delete; + + // Allow moving. + SafeRefPtr(SafeRefPtr&& aOther) noexcept : mRawPtr(aOther.mRawPtr) { + aOther.mRawPtr = nullptr; + } + SafeRefPtr& operator=(SafeRefPtr&& aOther) noexcept { + assign_assuming_AddRef(aOther.forget().take()); + return *this; + } + + ~SafeRefPtr() { + static_assert(!std::is_copy_constructible_v<T>); + static_assert(!std::is_copy_assignable_v<T>); + static_assert(!std::is_move_constructible_v<T>); + static_assert(!std::is_move_assignable_v<T>); + + if (mRawPtr) { + ConstRemovingRefPtrTraits<T>::Release(mRawPtr); + } + } + + typedef T element_type; + + explicit operator bool() const { return mRawPtr; } + bool operator!() const { return !mRawPtr; } + + T& operator*() const&& = delete; + + T& operator*() const& { + MOZ_ASSERT(mRawPtr); + return *mRawPtr; + } + + T* operator->() const&& = delete; + + T* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN { + MOZ_ASSERT(mRawPtr); + return mRawPtr; + } + + Maybe<T&> maybeDeref() const { return ToMaybeRef(mRawPtr); } + + T* unsafeGetRawPtr() const { return mRawPtr; } + + SafeRefPtr<T> clonePtr() const { + return SafeRefPtr{mRawPtr, AcquireStrongRefFromRawPtr{}}; + } + + already_AddRefed<T> forget() { + auto* const res = mRawPtr; + mRawPtr = nullptr; + return dont_AddRef(res); + } + + bool operator==(const SafeRefPtr<T>& aOther) const { + return mRawPtr == aOther.mRawPtr; + } + + bool operator!=(const SafeRefPtr<T>& aOther) const { + return mRawPtr != aOther.mRawPtr; + } + + template <typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>> + SafeRefPtr<U> downcast() && { + SafeRefPtr<U> res; + res.mRawPtr = static_cast<U*>(mRawPtr); + mRawPtr = nullptr; + return res; + } + + template <typename U> + friend RefPtr<U> AsRefPtr(SafeRefPtr<U>&& aSafeRefPtr); +}; + +template <typename T> +SafeRefPtr(RefPtr<T>&&) -> SafeRefPtr<T>; + +template <typename T> +SafeRefPtr(already_AddRefed<T>&&) -> SafeRefPtr<T>; + +template <typename T> +class CheckedUnsafePtr; + +template <typename T> +SafeRefPtr(const CheckedUnsafePtr<T>&, const AcquireStrongRefFromRawPtr&) + -> SafeRefPtr<T>; + +template <typename T> +SafeRefPtr<T>::SafeRefPtr(T* aRawPtr, detail::InitialConstructionTag) + : mRawPtr(aRawPtr) { + if (!std::is_base_of_v<detail::SafeRefCountedBase, T> && mRawPtr) { + ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr); + } +} + +template <typename T> +bool operator==(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) { + return !aRhs; +} + +template <typename T> +bool operator!=(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) { + return static_cast<bool>(aRhs); +} + +template <typename T> +bool operator==(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) { + return !aLhs; +} + +template <typename T> +bool operator!=(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) { + return static_cast<bool>(aLhs); +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator==(T* const aLhs, const SafeRefPtr<U>& aRhs) { + return aLhs == aRhs.unsafeGetRawPtr(); +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator!=(T* const aLhs, const SafeRefPtr<U>& aRhs) { + return !(aLhs == aRhs); +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator==(const SafeRefPtr<T>& aLhs, U* const aRhs) { + return aRhs == aLhs; +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator!=(const SafeRefPtr<T>& aLhs, U* const aRhs) { + return aRhs != aLhs; +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator==(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) { + return &aLhs.ref() == aRhs.unsafeGetRawPtr(); +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator!=(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) { + return !(aLhs == aRhs); +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator==(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) { + return aRhs == aLhs; +} + +template <typename T, typename U, typename = std::common_type_t<T*, U*>> +bool operator!=(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) { + return aRhs != aLhs; +} + +template <typename T> +RefPtr<T> AsRefPtr(SafeRefPtr<T>&& aSafeRefPtr) { + return aSafeRefPtr.forget(); +} + +template <typename T, typename... Args> +SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs) { + return SafeRefPtr{new T(std::forward<Args>(aArgs)...), + detail::InitialConstructionTag{}}; +} + +template <typename T> +void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + const SafeRefPtr<T>& aField, const char* aName, + uint32_t aFlags = 0) { + CycleCollectionNoteChild(aCallback, aField.unsafeGetRawPtr(), aName, aFlags); +} + +template <typename T> +void ImplCycleCollectionUnlink(SafeRefPtr<T>& aField) { + aField = nullptr; +} + +namespace detail { + +template <typename T, RefCountAtomicity Atomicity> +SafeRefPtr<T> SafeRefCounted<T, Atomicity>::SafeRefPtrFromThis() { + // this actually is safe + return {static_cast<T*>(this), AcquireStrongRefFromRawPtr{}}; +} + +template <typename T> +struct CopyablePtr<SafeRefPtr<T>> { + SafeRefPtr<T> mPtr; + + explicit CopyablePtr(SafeRefPtr<T> aPtr) : mPtr{std::move(aPtr)} {} + + CopyablePtr(const CopyablePtr& aOther) : mPtr{aOther.mPtr.clonePtr()} {} + CopyablePtr& operator=(const CopyablePtr& aOther) { + if (this != &aOther) { + mPtr = aOther.mPtr.clonePtr(); + } + return *this; + } + CopyablePtr(CopyablePtr&&) = default; + CopyablePtr& operator=(CopyablePtr&&) = default; +}; + +} // namespace detail + +namespace dom { +/// XXX Move this to BindingUtils.h later on +template <class T, class S> +inline RefPtr<T> StrongOrRawPtr(SafeRefPtr<S>&& aPtr) { + return AsRefPtr(std::move(aPtr)); +} + +} // namespace dom + +} // namespace mozilla + +template <class T> +class nsTObserverArray<mozilla::SafeRefPtr<T>> + : public nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0> { + public: + using base_type = nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0>; + using size_type = nsTObserverArray_base::size_type; + + // Initialization methods + nsTObserverArray() = default; + + // Initialize this array and pre-allocate some number of elements. + explicit nsTObserverArray(size_type aCapacity) { + base_type::mArray.SetCapacity(aCapacity); + } + + nsTObserverArray Clone() const { + auto result = nsTObserverArray{}; + result.mArray = mozilla::TransformIntoNewArray( + this->mArray, [](const auto& ptr) { return ptr.clonePtr(); }); + return result; + } +}; + +// Use MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED in a 'Class' derived from a +// 'Super' class which derives from (Atomic)SafeRefCounted, and from some other +// class using NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING. +#if defined(NS_BUILD_REFCNT_LOGGING) +# define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \ + template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \ + friend class ::mozilla::detail::SafeRefCounted; \ + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \ + NS_IMPL_ADDREF_INHERITED_GUTS(Class, Super); \ + } \ + NS_IMETHOD_(MozExternalRefCountType) Release() override { \ + NS_IMPL_RELEASE_INHERITED_GUTS(Class, Super); \ + } +#else // NS_BUILD_REFCNT_LOGGING +# define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \ + template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \ + friend class ::mozilla::detail::SafeRefCounted; \ + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \ + return Super::AddRef(); \ + } \ + NS_IMETHOD_(MozExternalRefCountType) Release() override { \ + return Super::Release(); \ + } +#endif + +#endif |