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