AtomicRefCountedWithFinalize.h (5306B)
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_ATOMICREFCOUNTEDWITHFINALIZE_H_ 8 #define MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_ 9 10 #include "mozilla/RefPtr.h" 11 #include "MainThreadUtils.h" 12 #include "base/message_loop.h" 13 #include "base/task.h" 14 #include "mozilla/gfx/Logging.h" 15 16 #define ADDREF_MANUALLY(obj) \ 17 (obj)->AddRefManually(__FUNCTION__, __FILE__, __LINE__) 18 #define RELEASE_MANUALLY(obj) \ 19 (obj)->ReleaseManually(__FUNCTION__, __FILE__, __LINE__) 20 21 namespace mozilla { 22 23 template <class U> 24 class StaticRefPtr; 25 26 namespace gl { 27 template <typename T> 28 class RefSet; 29 30 template <typename T> 31 class RefQueue; 32 } // namespace gl 33 34 template <typename T> 35 class AtomicRefCountedWithFinalize { 36 protected: 37 explicit AtomicRefCountedWithFinalize(const char* aName) 38 : mRecycleCallback(nullptr), 39 mClosure(nullptr), 40 mRefCount(0) 41 #ifdef DEBUG 42 , 43 mSpew(false), 44 mManualAddRefs(0), 45 mManualReleases(0) 46 #endif 47 #ifdef NS_BUILD_REFCNT_LOGGING 48 , 49 mName(aName) 50 #endif 51 { 52 } 53 54 ~AtomicRefCountedWithFinalize() { 55 if (mRefCount >= 0) { 56 gfxCriticalError() << "Deleting referenced object? " << mRefCount; 57 } 58 } 59 60 public: 61 // Mark user classes that are considered flawless. 62 template <class U> 63 friend class ::mozilla::StaticRefPtr; 64 65 template <class U> 66 friend struct mozilla::RefPtrTraits; 67 68 template <typename U> 69 friend class ::mozilla::gl::RefSet; 70 71 template <typename U> 72 friend class ::mozilla::gl::RefQueue; 73 74 // friend class mozilla::gl::SurfaceFactory; 75 76 void AddRefManually(const char* funcName, const char* fileName, 77 uint32_t lineNum) { 78 #ifdef DEBUG 79 uint32_t count = ++mManualAddRefs; 80 if (mSpew) { 81 printf_stderr("AddRefManually() #%u in %s at %s:%u\n", count, funcName, 82 fileName, lineNum); 83 } 84 #else 85 (void)funcName; 86 (void)fileName; 87 (void)lineNum; 88 #endif 89 AddRef(); 90 } 91 92 void ReleaseManually(const char* funcName, const char* fileName, 93 uint32_t lineNum) { 94 #ifdef DEBUG 95 uint32_t count = ++mManualReleases; 96 if (mSpew) { 97 printf_stderr("ReleaseManually() #%u in %s at %s:%u\n", count, funcName, 98 fileName, lineNum); 99 } 100 #else 101 (void)funcName; 102 (void)fileName; 103 (void)lineNum; 104 #endif 105 Release(); 106 } 107 108 private: 109 void AddRef() { 110 MOZ_ASSERT(mRefCount >= 0, "AddRef() during/after Finalize()/dtor."); 111 #ifdef NS_BUILD_REFCNT_LOGGING 112 int currCount = ++mRefCount; 113 NS_LOG_ADDREF(this, currCount, mName, sizeof(*this)); 114 #else 115 ++mRefCount; 116 #endif 117 } 118 119 void Release() { 120 MOZ_ASSERT(mRefCount > 0, "Release() during/after Finalize()/dtor."); 121 // Read mRecycleCallback early so that it does not get set to 122 // deleted memory, if the object is goes away. See bug 994903. 123 // This saves us in the case where there is no callback, so that 124 // we can do the "else if" below. 125 RecycleCallback recycleCallback = mRecycleCallback; 126 int currCount = --mRefCount; 127 if (currCount < 0) { 128 gfxCriticalError() << "Invalid reference count release" << currCount; 129 ++mRefCount; 130 return; 131 } 132 #ifdef NS_BUILD_REFCNT_LOGGING 133 NS_LOG_RELEASE(this, currCount, mName); 134 #endif 135 136 if (0 == currCount) { 137 mRefCount = detail::DEAD; 138 MOZ_ASSERT(IsDead()); 139 140 // Recycle listeners must call ClearRecycleCallback 141 // before releasing their strong reference. 142 if (mRecycleCallback) { 143 gfxCriticalError() << "About to release with valid callback"; 144 mRecycleCallback = nullptr; 145 } 146 147 MOZ_ASSERT(mManualAddRefs == mManualReleases); 148 149 T* derived = static_cast<T*>(this); 150 derived->Finalize(); 151 delete derived; 152 } else if (1 == currCount && recycleCallback) { 153 // There is nothing enforcing this in the code, except how the callers 154 // are being careful to never let the reference count go down if there 155 // is a callback. 156 MOZ_ASSERT(!IsDead()); 157 T* derived = static_cast<T*>(this); 158 recycleCallback(derived, mClosure); 159 } 160 } 161 162 public: 163 typedef void (*RecycleCallback)(T* aObject, void* aClosure); 164 /** 165 * Set a callback responsible for recycling this object 166 * before it is finalized. 167 */ 168 void SetRecycleCallback(RecycleCallback aCallback, void* aClosure) { 169 MOZ_ASSERT(!IsDead()); 170 mRecycleCallback = aCallback; 171 mClosure = aClosure; 172 } 173 void ClearRecycleCallback() { 174 MOZ_ASSERT(!IsDead()); 175 SetRecycleCallback(nullptr, nullptr); 176 } 177 178 bool HasRecycleCallback() const { 179 MOZ_ASSERT(!IsDead()); 180 return !!mRecycleCallback; 181 } 182 183 bool IsDead() const { return mRefCount < 0; } 184 185 bool HasOneRef() const { return mRefCount == 1; } 186 187 private: 188 RecycleCallback mRecycleCallback; 189 void* mClosure; 190 Atomic<int> mRefCount; 191 #ifdef DEBUG 192 public: 193 bool mSpew; 194 195 private: 196 Atomic<uint32_t> mManualAddRefs; 197 Atomic<uint32_t> mManualReleases; 198 #endif 199 #ifdef NS_BUILD_REFCNT_LOGGING 200 const char* mName; 201 #endif 202 }; 203 204 } // namespace mozilla 205 206 #endif