1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_saferefptr_h__
8 #define mozilla_saferefptr_h__
10 #include "mozilla/ArrayAlgorithm.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/NotNull.h"
13 #include "mozilla/RefCounted.h"
14 #include "mozilla/RefPtr.h"
16 #include "nsTObserverArray.h"
22 template <typename T
, typename
... Args
>
23 SafeRefPtr
<T
> MakeSafeRefPtr(Args
&&... aArgs
);
26 struct InitialConstructionTag
{};
28 class SafeRefCountedBase
{
29 template <typename U
, typename
... Args
>
30 friend SafeRefPtr
<U
> mozilla::MakeSafeRefPtr(Args
&&... aArgs
);
33 friend class SafeRefPtr
;
35 void* operator new(size_t aSize
) { return ::operator new(aSize
); }
38 void operator delete(void* aPtr
) { ::operator delete(aPtr
); }
41 void* operator new[](size_t) = delete;
44 // SafeRefCounted is similar to RefCounted, but they differ in their initial
45 // refcount (here 1), and the visibility of operator new (here private). The
46 // rest is mostly a copy of RefCounted.
47 template <typename T
, RefCountAtomicity Atomicity
>
48 class SafeRefCounted
: public SafeRefCountedBase
{
50 SafeRefCounted() = default;
52 ~SafeRefCounted() { MOZ_ASSERT(mRefCnt
== detail::DEAD
); }
56 // Compatibility with nsRefPtr.
57 MozRefCountType
AddRef() const {
58 // Note: this method must be thread safe for AtomicRefCounted.
59 MOZ_ASSERT(int32_t(mRefCnt
) >= 0);
60 const MozRefCountType cnt
= ++mRefCnt
;
61 detail::RefCountLogger::logAddRef(static_cast<const T
*>(this), cnt
);
65 MozRefCountType
Release() const {
66 // Note: this method must be thread safe for AtomicRefCounted.
67 MOZ_ASSERT(int32_t(mRefCnt
) > 0);
68 detail::RefCountLogger::ReleaseLogger
logger(static_cast<const T
*>(this));
69 const MozRefCountType cnt
= --mRefCnt
;
70 // Note: it's not safe to touch |this| after decrementing the refcount,
72 logger
.logRelease(cnt
);
74 // Because we have atomically decremented the refcount above, only
75 // one thread can get a 0 count here, so as long as we can assume that
76 // everything else in the system is accessing this object through
77 // RefPtrs, it's safe to access |this| here.
79 mRefCnt
= detail::DEAD
;
81 delete static_cast<const T
*>(this);
86 // Compatibility with wtf::RefPtr.
87 void ref() { AddRef(); }
88 void deref() { Release(); }
89 MozRefCountType
refCount() const { return mRefCnt
; }
90 bool hasOneRef() const {
91 MOZ_ASSERT(mRefCnt
> 0);
96 SafeRefPtr
<T
> SafeRefPtrFromThis();
99 mutable RC
<MozRefCountType
, Atomicity
> mRefCnt
=
100 RC
<MozRefCountType
, Atomicity
>{1};
102 } // namespace detail
104 template <typename T
>
106 : public detail::SafeRefCounted
<T
, detail::NonAtomicRefCount
> {
109 static_assert(std::is_base_of
<SafeRefCounted
, T
>::value
,
110 "T must derive from SafeRefCounted<T>");
114 template <typename T
>
115 class AtomicSafeRefCounted
116 : public detail::SafeRefCounted
<T
, detail::AtomicRefCount
> {
118 ~AtomicSafeRefCounted() {
119 static_assert(std::is_base_of
<AtomicSafeRefCounted
, T
>::value
,
120 "T must derive from AtomicSafeRefCounted<T>");
124 struct AcquireStrongRefFromRawPtr
{};
126 // XXX for Apple, clang::trivial_abi is probably also supported, but we need to
127 // find out the correct version number
128 #if defined(__clang__) && !defined(__apple_build_version__) && \
130 # define MOZ_TRIVIAL_ABI [[clang::trivial_abi]]
132 # define MOZ_TRIVIAL_ABI
135 // A restricted variant of mozilla::RefPtr<T>, which prohibits some unsafe or
136 // unperformant misuses, in particular:
137 // * It is not implicitly convertible from a raw pointer. Unsafe acquisitions
138 // from a raw pointer must be made using the verbose
139 // AcquireStrongRefFromRawPtr. To create a new object on the heap, use
141 // * It does not implicitly decay to a raw pointer. unsafeGetRawPtr() must be
144 // * It is not copyable, but must be explicitly copied using clonePtr().
145 // * Temporaries cannot be dereferenced using operator* or operator->.
146 template <typename T
>
147 class MOZ_IS_REFPTR MOZ_TRIVIAL_ABI SafeRefPtr
{
148 template <typename U
>
149 friend class SafeRefPtr
;
151 template <typename U
, typename
... Args
>
152 friend SafeRefPtr
<U
> mozilla::MakeSafeRefPtr(Args
&&... aArgs
);
154 T
* MOZ_OWNING_REF mRawPtr
= nullptr;
156 // BEGIN Some things copied from RefPtr.
157 // We cannot simply use a RefPtr member because we want to be trivial_abi,
158 // which RefPtr is not.
159 void assign_with_AddRef(T
* aRawPtr
) {
161 ConstRemovingRefPtrTraits
<T
>::AddRef(aRawPtr
);
163 assign_assuming_AddRef(aRawPtr
);
166 void assign_assuming_AddRef(T
* aNewPtr
) {
170 ConstRemovingRefPtrTraits
<T
>::Release(oldPtr
);
175 struct ConstRemovingRefPtrTraits
{
176 static void AddRef(U
* aPtr
) { mozilla::RefPtrTraits
<U
>::AddRef(aPtr
); }
177 static void Release(U
* aPtr
) { mozilla::RefPtrTraits
<U
>::Release(aPtr
); }
180 struct ConstRemovingRefPtrTraits
<const U
> {
181 static void AddRef(const U
* aPtr
) {
182 mozilla::RefPtrTraits
<U
>::AddRef(const_cast<U
*>(aPtr
));
184 static void Release(const U
* aPtr
) {
185 mozilla::RefPtrTraits
<U
>::Release(const_cast<U
*>(aPtr
));
188 // END Some things copied from RefPtr.
190 SafeRefPtr(T
* aRawPtr
, mozilla::detail::InitialConstructionTag
);
193 SafeRefPtr() = default;
195 template <typename U
,
196 typename
= std::enable_if_t
<std::is_convertible_v
<U
*, T
*>>>
197 MOZ_IMPLICIT
SafeRefPtr(SafeRefPtr
<U
>&& aSrc
) : mRawPtr(aSrc
.mRawPtr
) {
198 aSrc
.mRawPtr
= nullptr;
201 explicit SafeRefPtr(RefPtr
<T
>&& aRefPtr
) : mRawPtr(aRefPtr
.forget().take()) {}
203 // To prevent implicit conversion of raw pointer to RefPtr and then
204 // calling the previous overload.
205 SafeRefPtr(T
* const aRawPtr
) = delete;
207 SafeRefPtr(T
* const aRawPtr
, const AcquireStrongRefFromRawPtr
&) {
208 assign_with_AddRef(aRawPtr
);
211 MOZ_IMPLICIT
SafeRefPtr(std::nullptr_t
) {}
213 // Prevent implicit copying, use clonePtr() instead.
214 SafeRefPtr(const SafeRefPtr
&) = delete;
215 SafeRefPtr
& operator=(const SafeRefPtr
&) = delete;
218 SafeRefPtr(SafeRefPtr
&& aOther
) noexcept
: mRawPtr(aOther
.mRawPtr
) {
219 aOther
.mRawPtr
= nullptr;
221 SafeRefPtr
& operator=(SafeRefPtr
&& aOther
) noexcept
{
222 assign_assuming_AddRef(aOther
.forget().take());
227 static_assert(!std::is_copy_constructible_v
<T
>);
228 static_assert(!std::is_copy_assignable_v
<T
>);
229 static_assert(!std::is_move_constructible_v
<T
>);
230 static_assert(!std::is_move_assignable_v
<T
>);
233 ConstRemovingRefPtrTraits
<T
>::Release(mRawPtr
);
237 typedef T element_type
;
239 explicit operator bool() const { return mRawPtr
; }
240 bool operator!() const { return !mRawPtr
; }
242 T
& operator*() const&& = delete;
244 T
& operator*() const& {
249 T
* operator->() const&& = delete;
251 T
* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
256 Maybe
<T
&> maybeDeref() const { return ToMaybeRef(mRawPtr
); }
258 T
* unsafeGetRawPtr() const { return mRawPtr
; }
260 SafeRefPtr
<T
> clonePtr() const {
261 return SafeRefPtr
{mRawPtr
, AcquireStrongRefFromRawPtr
{}};
264 already_AddRefed
<T
> forget() {
265 auto* const res
= mRawPtr
;
267 return dont_AddRef(res
);
270 bool operator==(const SafeRefPtr
<T
>& aOther
) const {
271 return mRawPtr
== aOther
.mRawPtr
;
274 bool operator!=(const SafeRefPtr
<T
>& aOther
) const {
275 return mRawPtr
!= aOther
.mRawPtr
;
278 template <typename U
, typename
= std::enable_if_t
<std::is_base_of_v
<T
, U
>>>
279 SafeRefPtr
<U
> downcast() && {
281 res
.mRawPtr
= static_cast<U
*>(mRawPtr
);
286 template <typename U
>
287 friend RefPtr
<U
> AsRefPtr(SafeRefPtr
<U
>&& aSafeRefPtr
);
290 template <typename T
>
291 SafeRefPtr(RefPtr
<T
>&&) -> SafeRefPtr
<T
>;
293 template <typename T
>
294 SafeRefPtr(already_AddRefed
<T
>&&) -> SafeRefPtr
<T
>;
296 template <typename T
>
297 class CheckedUnsafePtr
;
299 template <typename T
>
300 SafeRefPtr(const CheckedUnsafePtr
<T
>&,
301 const AcquireStrongRefFromRawPtr
&) -> SafeRefPtr
<T
>;
303 template <typename T
>
304 SafeRefPtr
<T
>::SafeRefPtr(T
* aRawPtr
, detail::InitialConstructionTag
)
306 if (!std::is_base_of_v
<detail::SafeRefCountedBase
, T
> && mRawPtr
) {
307 ConstRemovingRefPtrTraits
<T
>::AddRef(mRawPtr
);
311 template <typename T
>
312 bool operator==(std::nullptr_t aLhs
, const SafeRefPtr
<T
>& aRhs
) {
316 template <typename T
>
317 bool operator!=(std::nullptr_t aLhs
, const SafeRefPtr
<T
>& aRhs
) {
318 return static_cast<bool>(aRhs
);
321 template <typename T
>
322 bool operator==(const SafeRefPtr
<T
>& aLhs
, std::nullptr_t aRhs
) {
326 template <typename T
>
327 bool operator!=(const SafeRefPtr
<T
>& aLhs
, std::nullptr_t aRhs
) {
328 return static_cast<bool>(aLhs
);
331 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
332 bool operator==(T
* const aLhs
, const SafeRefPtr
<U
>& aRhs
) {
333 return aLhs
== aRhs
.unsafeGetRawPtr();
336 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
337 bool operator!=(T
* const aLhs
, const SafeRefPtr
<U
>& aRhs
) {
338 return !(aLhs
== aRhs
);
341 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
342 bool operator==(const SafeRefPtr
<T
>& aLhs
, U
* const aRhs
) {
346 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
347 bool operator!=(const SafeRefPtr
<T
>& aLhs
, U
* const aRhs
) {
351 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
352 bool operator==(const Maybe
<T
&> aLhs
, const SafeRefPtr
<U
>& aRhs
) {
353 return &aLhs
.ref() == aRhs
.unsafeGetRawPtr();
356 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
357 bool operator!=(const Maybe
<T
&> aLhs
, const SafeRefPtr
<U
>& aRhs
) {
358 return !(aLhs
== aRhs
);
361 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
362 bool operator==(const SafeRefPtr
<T
>& aLhs
, const Maybe
<U
&> aRhs
) {
366 template <typename T
, typename U
, typename
= std::common_type_t
<T
*, U
*>>
367 bool operator!=(const SafeRefPtr
<T
>& aLhs
, const Maybe
<U
&> aRhs
) {
371 template <typename T
>
372 RefPtr
<T
> AsRefPtr(SafeRefPtr
<T
>&& aSafeRefPtr
) {
373 return aSafeRefPtr
.forget();
376 template <typename T
, typename
... Args
>
377 SafeRefPtr
<T
> MakeSafeRefPtr(Args
&&... aArgs
) {
378 return SafeRefPtr
{new T(std::forward
<Args
>(aArgs
)...),
379 detail::InitialConstructionTag
{}};
382 template <typename T
>
383 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback
& aCallback
,
384 const SafeRefPtr
<T
>& aField
, const char* aName
,
385 uint32_t aFlags
= 0) {
386 CycleCollectionNoteChild(aCallback
, aField
.unsafeGetRawPtr(), aName
, aFlags
);
389 template <typename T
>
390 void ImplCycleCollectionUnlink(SafeRefPtr
<T
>& aField
) {
396 template <typename T
, RefCountAtomicity Atomicity
>
397 SafeRefPtr
<T
> SafeRefCounted
<T
, Atomicity
>::SafeRefPtrFromThis() {
398 // this actually is safe
399 return {static_cast<T
*>(this), AcquireStrongRefFromRawPtr
{}};
402 template <typename T
>
403 struct CopyablePtr
<SafeRefPtr
<T
>> {
406 explicit CopyablePtr(SafeRefPtr
<T
> aPtr
) : mPtr
{std::move(aPtr
)} {}
408 CopyablePtr(const CopyablePtr
& aOther
) : mPtr
{aOther
.mPtr
.clonePtr()} {}
409 CopyablePtr
& operator=(const CopyablePtr
& aOther
) {
410 if (this != &aOther
) {
411 mPtr
= aOther
.mPtr
.clonePtr();
415 CopyablePtr(CopyablePtr
&&) = default;
416 CopyablePtr
& operator=(CopyablePtr
&&) = default;
419 } // namespace detail
422 /// XXX Move this to BindingUtils.h later on
423 template <class T
, class S
>
424 inline RefPtr
<T
> StrongOrRawPtr(SafeRefPtr
<S
>&& aPtr
) {
425 return AsRefPtr(std::move(aPtr
));
430 } // namespace mozilla
433 class nsTObserverArray
<mozilla::SafeRefPtr
<T
>>
434 : public nsAutoTObserverArray
<mozilla::SafeRefPtr
<T
>, 0> {
436 using base_type
= nsAutoTObserverArray
<mozilla::SafeRefPtr
<T
>, 0>;
437 using size_type
= nsTObserverArray_base::size_type
;
439 // Initialization methods
440 nsTObserverArray() = default;
442 // Initialize this array and pre-allocate some number of elements.
443 explicit nsTObserverArray(size_type aCapacity
) {
444 base_type::mArray
.SetCapacity(aCapacity
);
447 nsTObserverArray
Clone() const {
448 auto result
= nsTObserverArray
{};
449 result
.mArray
= mozilla::TransformIntoNewArray(
450 this->mArray
, [](const auto& ptr
) { return ptr
.clonePtr(); });
455 // Use MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED in a 'Class' derived from a
456 // 'Super' class which derives from (Atomic)SafeRefCounted, and from some other
457 // class using NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING.
458 #if defined(NS_BUILD_REFCNT_LOGGING)
459 # define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \
460 template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \
461 friend class ::mozilla::detail::SafeRefCounted; \
462 NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \
463 NS_IMPL_ADDREF_INHERITED_GUTS(Class, Super); \
465 NS_IMETHOD_(MozExternalRefCountType) Release() override { \
466 NS_IMPL_RELEASE_INHERITED_GUTS(Class, Super); \
468 #else // NS_BUILD_REFCNT_LOGGING
469 # define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \
470 template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \
471 friend class ::mozilla::detail::SafeRefCounted; \
472 NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \
473 return Super::AddRef(); \
475 NS_IMETHOD_(MozExternalRefCountType) Release() override { \
476 return Super::Release(); \