SafeRefPtr.h (14833B)
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/. */ 6 7 #ifndef mozilla_saferefptr_h__ 8 #define mozilla_saferefptr_h__ 9 10 #include "mozilla/ArrayAlgorithm.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/RefCounted.h" 13 #include "mozilla/RefPtr.h" 14 #include "nsCOMPtr.h" 15 #include "nsTObserverArray.h" 16 17 namespace mozilla { 18 template <typename T> 19 class SafeRefPtr; 20 21 template <typename T, typename... Args> 22 SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs); 23 24 namespace detail { 25 struct InitialConstructionTag {}; 26 27 class SafeRefCountedBase { 28 template <typename U, typename... Args> 29 friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs); 30 31 template <typename T> 32 friend class SafeRefPtr; 33 34 void* operator new(size_t aSize) { return ::operator new(aSize); } 35 36 protected: 37 void operator delete(void* aPtr) { ::operator delete(aPtr); } 38 39 public: 40 void* operator new[](size_t) = delete; 41 }; 42 43 // SafeRefCounted is similar to RefCounted, but they differ in their initial 44 // refcount (here 1), and the visibility of operator new (here private). The 45 // rest is mostly a copy of RefCounted. 46 template <typename T, RefCountAtomicity Atomicity> 47 class SafeRefCounted : public SafeRefCountedBase { 48 protected: 49 SafeRefCounted() = default; 50 #ifdef DEBUG 51 ~SafeRefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); } 52 #endif 53 54 public: 55 // Compatibility with nsRefPtr. 56 MozRefCountType AddRef() const { 57 // Note: this method must be thread safe for AtomicRefCounted. 58 MOZ_ASSERT(int32_t(mRefCnt) >= 0); 59 const MozRefCountType cnt = ++mRefCnt; 60 detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt); 61 return cnt; 62 } 63 64 MozRefCountType Release() const { 65 // Note: this method must be thread safe for AtomicRefCounted. 66 MOZ_ASSERT(int32_t(mRefCnt) > 0); 67 detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this)); 68 const MozRefCountType cnt = --mRefCnt; 69 // Note: it's not safe to touch |this| after decrementing the refcount, 70 // except for below. 71 logger.logRelease(cnt); 72 if (0 == cnt) { 73 // Because we have atomically decremented the refcount above, only 74 // one thread can get a 0 count here, so as long as we can assume that 75 // everything else in the system is accessing this object through 76 // RefPtrs, it's safe to access |this| here. 77 #ifdef DEBUG 78 mRefCnt = detail::DEAD; 79 #endif 80 delete static_cast<const T*>(this); 81 } 82 return cnt; 83 } 84 85 // Compatibility with wtf::RefPtr. 86 void ref() { AddRef(); } 87 void deref() { Release(); } 88 MozRefCountType refCount() const { return mRefCnt; } 89 bool hasOneRef() const { 90 MOZ_ASSERT(mRefCnt > 0); 91 return mRefCnt == 1; 92 } 93 94 protected: 95 SafeRefPtr<T> SafeRefPtrFromThis(); 96 97 private: 98 mutable RC<MozRefCountType, Atomicity> mRefCnt = 99 RC<MozRefCountType, Atomicity>{1}; 100 }; 101 } // namespace detail 102 103 template <typename T> 104 class SafeRefCounted 105 : public detail::SafeRefCounted<T, detail::NonAtomicRefCount> { 106 public: 107 ~SafeRefCounted() { 108 static_assert(std::is_base_of<SafeRefCounted, T>::value, 109 "T must derive from SafeRefCounted<T>"); 110 } 111 }; 112 113 template <typename T> 114 class AtomicSafeRefCounted 115 : public detail::SafeRefCounted<T, detail::AtomicRefCount> { 116 public: 117 ~AtomicSafeRefCounted() { 118 static_assert(std::is_base_of<AtomicSafeRefCounted, T>::value, 119 "T must derive from AtomicSafeRefCounted<T>"); 120 } 121 }; 122 123 struct AcquireStrongRefFromRawPtr {}; 124 125 // XXX for Apple, clang::trivial_abi is probably also supported, but we need to 126 // find out the correct version number 127 #if defined(__clang__) && !defined(__apple_build_version__) && \ 128 __clang_major__ >= 7 129 # define MOZ_TRIVIAL_ABI [[clang::trivial_abi]] 130 #else 131 # define MOZ_TRIVIAL_ABI 132 #endif 133 134 // A restricted variant of mozilla::RefPtr<T>, which prohibits some unsafe or 135 // unperformant misuses, in particular: 136 // * It is not implicitly convertible from a raw pointer. Unsafe acquisitions 137 // from a raw pointer must be made using the verbose 138 // AcquireStrongRefFromRawPtr. To create a new object on the heap, use 139 // MakeSafeRefPtr. 140 // * It does not implicitly decay to a raw pointer. unsafeGetRawPtr() must be 141 // called 142 // explicitly. 143 // * It is not copyable, but must be explicitly copied using clonePtr(). 144 // * Temporaries cannot be dereferenced using operator* or operator->. 145 template <typename T> 146 class MOZ_IS_REFPTR MOZ_TRIVIAL_ABI SafeRefPtr { 147 template <typename U> 148 friend class SafeRefPtr; 149 150 template <typename U, typename... Args> 151 friend SafeRefPtr<U> mozilla::MakeSafeRefPtr(Args&&... aArgs); 152 153 T* MOZ_OWNING_REF mRawPtr = nullptr; 154 155 // BEGIN Some things copied from RefPtr. 156 // We cannot simply use a RefPtr member because we want to be trivial_abi, 157 // which RefPtr is not. 158 void assign_with_AddRef(T* aRawPtr) { 159 if (aRawPtr) { 160 ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr); 161 } 162 assign_assuming_AddRef(aRawPtr); 163 } 164 165 void assign_assuming_AddRef(T* aNewPtr) { 166 T* oldPtr = mRawPtr; 167 mRawPtr = aNewPtr; 168 if (oldPtr) { 169 ConstRemovingRefPtrTraits<T>::Release(oldPtr); 170 } 171 } 172 173 template <class U> 174 struct ConstRemovingRefPtrTraits { 175 static void AddRef(U* aPtr) { mozilla::RefPtrTraits<U>::AddRef(aPtr); } 176 static void Release(U* aPtr) { mozilla::RefPtrTraits<U>::Release(aPtr); } 177 }; 178 template <class U> 179 struct ConstRemovingRefPtrTraits<const U> { 180 static void AddRef(const U* aPtr) { 181 mozilla::RefPtrTraits<U>::AddRef(const_cast<U*>(aPtr)); 182 } 183 static void Release(const U* aPtr) { 184 mozilla::RefPtrTraits<U>::Release(const_cast<U*>(aPtr)); 185 } 186 }; 187 // END Some things copied from RefPtr. 188 189 SafeRefPtr(T* aRawPtr, mozilla::detail::InitialConstructionTag); 190 191 public: 192 SafeRefPtr() = default; 193 194 template <typename U, 195 typename = std::enable_if_t<std::is_convertible_v<U*, T*>>> 196 MOZ_IMPLICIT SafeRefPtr(SafeRefPtr<U>&& aSrc) : mRawPtr(aSrc.mRawPtr) { 197 aSrc.mRawPtr = nullptr; 198 } 199 200 explicit SafeRefPtr(RefPtr<T>&& aRefPtr) : mRawPtr(aRefPtr.forget().take()) {} 201 202 // To prevent implicit conversion of raw pointer to RefPtr and then 203 // calling the previous overload. 204 SafeRefPtr(T* const aRawPtr) = delete; 205 206 SafeRefPtr(T* const aRawPtr, const AcquireStrongRefFromRawPtr&) { 207 assign_with_AddRef(aRawPtr); 208 } 209 210 MOZ_IMPLICIT SafeRefPtr(std::nullptr_t) {} 211 212 // Prevent implicit copying, use clonePtr() instead. 213 SafeRefPtr(const SafeRefPtr&) = delete; 214 SafeRefPtr& operator=(const SafeRefPtr&) = delete; 215 216 // Allow moving. 217 SafeRefPtr(SafeRefPtr&& aOther) noexcept : mRawPtr(aOther.mRawPtr) { 218 aOther.mRawPtr = nullptr; 219 } 220 SafeRefPtr& operator=(SafeRefPtr&& aOther) noexcept { 221 assign_assuming_AddRef(aOther.forget().take()); 222 return *this; 223 } 224 225 ~SafeRefPtr() { 226 static_assert(!std::is_copy_constructible_v<T>); 227 static_assert(!std::is_copy_assignable_v<T>); 228 static_assert(!std::is_move_constructible_v<T>); 229 static_assert(!std::is_move_assignable_v<T>); 230 231 if (mRawPtr) { 232 ConstRemovingRefPtrTraits<T>::Release(mRawPtr); 233 } 234 } 235 236 typedef T element_type; 237 238 explicit operator bool() const { return mRawPtr; } 239 bool operator!() const { return !mRawPtr; } 240 241 T& operator*() const&& = delete; 242 243 T& operator*() const& { 244 MOZ_ASSERT(mRawPtr); 245 return *mRawPtr; 246 } 247 248 T* operator->() const&& = delete; 249 250 T* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN { 251 MOZ_ASSERT(mRawPtr); 252 return mRawPtr; 253 } 254 255 Maybe<T&> maybeDeref() const { return ToMaybeRef(mRawPtr); } 256 257 T* unsafeGetRawPtr() const { return mRawPtr; } 258 259 SafeRefPtr<T> clonePtr() const { 260 return SafeRefPtr{mRawPtr, AcquireStrongRefFromRawPtr{}}; 261 } 262 263 already_AddRefed<T> forget() { 264 auto* const res = mRawPtr; 265 mRawPtr = nullptr; 266 return dont_AddRef(res); 267 } 268 269 bool operator==(const SafeRefPtr<T>& aOther) const { 270 return mRawPtr == aOther.mRawPtr; 271 } 272 273 bool operator!=(const SafeRefPtr<T>& aOther) const { 274 return mRawPtr != aOther.mRawPtr; 275 } 276 277 template <typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>> 278 SafeRefPtr<U> downcast() && { 279 SafeRefPtr<U> res; 280 res.mRawPtr = static_cast<U*>(mRawPtr); 281 mRawPtr = nullptr; 282 return res; 283 } 284 285 template <typename U> 286 friend RefPtr<U> AsRefPtr(SafeRefPtr<U>&& aSafeRefPtr); 287 }; 288 289 template <typename T> 290 SafeRefPtr(RefPtr<T>&&) -> SafeRefPtr<T>; 291 292 template <typename T> 293 SafeRefPtr(already_AddRefed<T>&&) -> SafeRefPtr<T>; 294 295 template <typename T> 296 class CheckedUnsafePtr; 297 298 template <typename T> 299 SafeRefPtr(const CheckedUnsafePtr<T>&, const AcquireStrongRefFromRawPtr&) 300 -> SafeRefPtr<T>; 301 302 template <typename T> 303 SafeRefPtr<T>::SafeRefPtr(T* aRawPtr, detail::InitialConstructionTag) 304 : mRawPtr(aRawPtr) { 305 if (!std::is_base_of_v<detail::SafeRefCountedBase, T> && mRawPtr) { 306 ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr); 307 } 308 } 309 310 template <typename T> 311 bool operator==(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) { 312 return !aRhs; 313 } 314 315 template <typename T> 316 bool operator!=(std::nullptr_t aLhs, const SafeRefPtr<T>& aRhs) { 317 return static_cast<bool>(aRhs); 318 } 319 320 template <typename T> 321 bool operator==(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) { 322 return !aLhs; 323 } 324 325 template <typename T> 326 bool operator!=(const SafeRefPtr<T>& aLhs, std::nullptr_t aRhs) { 327 return static_cast<bool>(aLhs); 328 } 329 330 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 331 bool operator==(T* const aLhs, const SafeRefPtr<U>& aRhs) { 332 return aLhs == aRhs.unsafeGetRawPtr(); 333 } 334 335 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 336 bool operator!=(T* const aLhs, const SafeRefPtr<U>& aRhs) { 337 return !(aLhs == aRhs); 338 } 339 340 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 341 bool operator==(const SafeRefPtr<T>& aLhs, U* const aRhs) { 342 return aRhs == aLhs; 343 } 344 345 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 346 bool operator!=(const SafeRefPtr<T>& aLhs, U* const aRhs) { 347 return aRhs != aLhs; 348 } 349 350 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 351 bool operator==(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) { 352 return &aLhs.ref() == aRhs.unsafeGetRawPtr(); 353 } 354 355 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 356 bool operator!=(const Maybe<T&> aLhs, const SafeRefPtr<U>& aRhs) { 357 return !(aLhs == aRhs); 358 } 359 360 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 361 bool operator==(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) { 362 return aRhs == aLhs; 363 } 364 365 template <typename T, typename U, typename = std::common_type_t<T*, U*>> 366 bool operator!=(const SafeRefPtr<T>& aLhs, const Maybe<U&> aRhs) { 367 return aRhs != aLhs; 368 } 369 370 template <typename T> 371 RefPtr<T> AsRefPtr(SafeRefPtr<T>&& aSafeRefPtr) { 372 return aSafeRefPtr.forget(); 373 } 374 375 template <typename T, typename... Args> 376 SafeRefPtr<T> MakeSafeRefPtr(Args&&... aArgs) { 377 return SafeRefPtr{new T(std::forward<Args>(aArgs)...), 378 detail::InitialConstructionTag{}}; 379 } 380 381 template <typename T> 382 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, 383 const SafeRefPtr<T>& aField, const char* aName, 384 uint32_t aFlags = 0) { 385 CycleCollectionNoteChild(aCallback, aField.unsafeGetRawPtr(), aName, aFlags); 386 } 387 388 template <typename T> 389 void ImplCycleCollectionUnlink(SafeRefPtr<T>& aField) { 390 aField = nullptr; 391 } 392 393 namespace detail { 394 395 template <typename T, RefCountAtomicity Atomicity> 396 SafeRefPtr<T> SafeRefCounted<T, Atomicity>::SafeRefPtrFromThis() { 397 // this actually is safe 398 return {static_cast<T*>(this), AcquireStrongRefFromRawPtr{}}; 399 } 400 401 template <typename T> 402 struct CopyablePtr<SafeRefPtr<T>> { 403 SafeRefPtr<T> mPtr; 404 405 explicit CopyablePtr(SafeRefPtr<T> aPtr) : mPtr{std::move(aPtr)} {} 406 407 CopyablePtr(const CopyablePtr& aOther) : mPtr{aOther.mPtr.clonePtr()} {} 408 CopyablePtr& operator=(const CopyablePtr& aOther) { 409 if (this != &aOther) { 410 mPtr = aOther.mPtr.clonePtr(); 411 } 412 return *this; 413 } 414 CopyablePtr(CopyablePtr&&) = default; 415 CopyablePtr& operator=(CopyablePtr&&) = default; 416 }; 417 418 } // namespace detail 419 420 namespace dom { 421 /// XXX Move this to BindingUtils.h later on 422 template <class T, class S> 423 inline RefPtr<T> StrongOrRawPtr(SafeRefPtr<S>&& aPtr) { 424 return AsRefPtr(std::move(aPtr)); 425 } 426 427 } // namespace dom 428 429 } // namespace mozilla 430 431 template <class T> 432 class nsTObserverArray<mozilla::SafeRefPtr<T>> 433 : public nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0> { 434 public: 435 using base_type = nsAutoTObserverArray<mozilla::SafeRefPtr<T>, 0>; 436 using size_type = nsTObserverArray_base::size_type; 437 438 // Initialization methods 439 nsTObserverArray() = default; 440 441 // Initialize this array and pre-allocate some number of elements. 442 explicit nsTObserverArray(size_type aCapacity) { 443 base_type::mArray.SetCapacity(aCapacity); 444 } 445 446 nsTObserverArray Clone() const { 447 auto result = nsTObserverArray{}; 448 result.mArray = mozilla::TransformIntoNewArray( 449 this->mArray, [](const auto& ptr) { return ptr.clonePtr(); }); 450 return result; 451 } 452 }; 453 454 // Use MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED in a 'Class' derived from a 455 // 'Super' class which derives from (Atomic)SafeRefCounted, and from some other 456 // class using NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING. 457 #if defined(NS_BUILD_REFCNT_LOGGING) 458 # define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \ 459 template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \ 460 friend class ::mozilla::detail::SafeRefCounted; \ 461 NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \ 462 NS_IMPL_ADDREF_INHERITED_GUTS(Class, Super); \ 463 } \ 464 NS_IMETHOD_(MozExternalRefCountType) Release() override { \ 465 NS_IMPL_RELEASE_INHERITED_GUTS(Class, Super); \ 466 } 467 #else // NS_BUILD_REFCNT_LOGGING 468 # define MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Class, Super) \ 469 template <typename T, ::mozilla::detail::RefCountAtomicity Atomicity> \ 470 friend class ::mozilla::detail::SafeRefCounted; \ 471 NS_IMETHOD_(MozExternalRefCountType) AddRef() override { \ 472 return Super::AddRef(); \ 473 } \ 474 NS_IMETHOD_(MozExternalRefCountType) Release() override { \ 475 return Super::Release(); \ 476 } 477 #endif 478 479 #endif