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 // This header provides virtual, non-templated alternatives to MFBT's 8 // RefCounted<T>. It intentionally uses MFBT coding style with the intention of 9 // moving there should there be other use cases for it. 10 11 #ifndef MOZILLA_GENERICREFCOUNTED_H_ 12 #define MOZILLA_GENERICREFCOUNTED_H_ 13 14 #include <type_traits> 15 16 #include "mozilla/RefPtr.h" 17 #include "mozilla/RefCounted.h" 18 19 namespace mozilla { 20 21 /** 22 * Common base class for GenericRefCounted and GenericAtomicRefCounted. 23 * 24 * Having this shared base class, common to both the atomic and non-atomic 25 * cases, allows to have RefPtr's that don't care about whether the 26 * objects they're managing have atomic refcounts or not. 27 */ 28 class GenericRefCountedBase { 29 protected: 30 virtual ~GenericRefCountedBase() = default; 31 32 public: 33 // AddRef() and Release() method names are for compatibility with nsRefPtr. 34 virtual void AddRef() = 0; 35 36 virtual void Release() = 0; 37 38 // ref() and deref() method names are for compatibility with wtf::RefPtr. 39 // No virtual keywords here: if a subclass wants to override the refcounting 40 // mechanism, it is welcome to do so by overriding AddRef() and Release(). ref()41 void ref() { AddRef(); } deref()42 void deref() { Release(); } 43 44 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING 45 virtual const char* typeName() const = 0; 46 virtual size_t typeSize() const = 0; 47 #endif 48 }; 49 50 namespace detail { 51 52 template <RefCountAtomicity Atomicity> 53 class GenericRefCounted : public GenericRefCountedBase { 54 protected: GenericRefCounted()55 GenericRefCounted() : refCnt(0) {} 56 ~GenericRefCounted()57 virtual ~GenericRefCounted() { MOZ_ASSERT(refCnt == detail::DEAD); } 58 59 public: AddRef()60 virtual void AddRef() override { 61 // Note: this method must be thread safe for GenericAtomicRefCounted. 62 MOZ_ASSERT(int32_t(refCnt) >= 0); 63 MozRefCountType cnt = ++refCnt; 64 detail::RefCountLogger::logAddRef(this, cnt); 65 } 66 Release()67 virtual void Release() override { 68 // Note: this method must be thread safe for GenericAtomicRefCounted. 69 MOZ_ASSERT(int32_t(refCnt) > 0); 70 detail::RefCountLogger::ReleaseLogger logger{this}; 71 MozRefCountType cnt = --refCnt; 72 // Note: it's not safe to touch |this| after decrementing the refcount, 73 // except for below. 74 logger.logRelease(cnt); 75 if (0 == cnt) { 76 // Because we have atomically decremented the refcount above, only 77 // one thread can get a 0 count here, so as long as we can assume that 78 // everything else in the system is accessing this object through 79 // RefPtrs, it's safe to access |this| here. 80 #ifdef DEBUG 81 refCnt = detail::DEAD; 82 #endif 83 delete this; 84 } 85 } 86 refCount()87 MozRefCountType refCount() const { return refCnt; } hasOneRef()88 bool hasOneRef() const { 89 MOZ_ASSERT(refCnt > 0); 90 return refCnt == 1; 91 } 92 93 private: 94 std::conditional_t<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, 95 MozRefCountType> 96 refCnt; 97 }; 98 99 } // namespace detail 100 101 /** 102 * This reference-counting base class is virtual instead of 103 * being templated, which is useful in cases where one needs 104 * genericity at binary code level, but comes at the cost 105 * of a moderate performance and size overhead, like anything virtual. 106 */ 107 class GenericRefCounted 108 : public detail::GenericRefCounted<detail::NonAtomicRefCount> {}; 109 110 /** 111 * GenericAtomicRefCounted is like GenericRefCounted, with an atomically updated 112 * reference counter. 113 */ 114 class GenericAtomicRefCounted 115 : public detail::GenericRefCounted<detail::AtomicRefCount> {}; 116 117 } // namespace mozilla 118 119 #endif 120