/* -*- 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");
}
};
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");
}
};
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