Line data Source code
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 : /* CRTP refcounting templates. Do not use unless you are an Expert. */
8 :
9 : #ifndef mozilla_RefCounted_h
10 : #define mozilla_RefCounted_h
11 :
12 : #include "mozilla/AlreadyAddRefed.h"
13 : #include "mozilla/Assertions.h"
14 : #include "mozilla/Atomics.h"
15 : #include "mozilla/Attributes.h"
16 : #include "mozilla/Move.h"
17 : #include "mozilla/RefCountType.h"
18 : #include "mozilla/TypeTraits.h"
19 :
20 : #if defined(MOZILLA_INTERNAL_API)
21 : #include "nsXPCOM.h"
22 : #endif
23 :
24 : #if defined(MOZILLA_INTERNAL_API) && \
25 : (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
26 : #define MOZ_REFCOUNTED_LEAK_CHECKING
27 : #endif
28 :
29 : namespace mozilla {
30 :
31 : /**
32 : * RefCounted<T> is a sort of a "mixin" for a class T. RefCounted
33 : * manages, well, refcounting for T, and because RefCounted is
34 : * parameterized on T, RefCounted<T> can call T's destructor directly.
35 : * This means T doesn't need to have a virtual dtor and so doesn't
36 : * need a vtable.
37 : *
38 : * RefCounted<T> is created with refcount == 0. Newly-allocated
39 : * RefCounted<T> must immediately be assigned to a RefPtr to make the
40 : * refcount > 0. It's an error to allocate and free a bare
41 : * RefCounted<T>, i.e. outside of the RefPtr machinery. Attempts to
42 : * do so will abort DEBUG builds.
43 : *
44 : * Live RefCounted<T> have refcount > 0. The lifetime (refcounts) of
45 : * live RefCounted<T> are controlled by RefPtr<T> and
46 : * RefPtr<super/subclass of T>. Upon a transition from refcounted==1
47 : * to 0, the RefCounted<T> "dies" and is destroyed. The "destroyed"
48 : * state is represented in DEBUG builds by refcount==0xffffdead. This
49 : * state distinguishes use-before-ref (refcount==0) from
50 : * use-after-destroy (refcount==0xffffdead).
51 : *
52 : * Note that when deriving from RefCounted or AtomicRefCounted, you
53 : * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public
54 : * section of your class, where ClassName is the name of your class.
55 : *
56 : * Note: SpiderMonkey should use js::RefCounted instead since that type
57 : * will use appropriate js_delete and also not break ref-count logging.
58 : */
59 : namespace detail {
60 : const MozRefCountType DEAD = 0xffffdead;
61 :
62 : // When building code that gets compiled into Gecko, try to use the
63 : // trace-refcount leak logging facilities.
64 : #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
65 : class RefCountLogger
66 : {
67 : public:
68 7036 : static void logAddRef(const void* aPointer, MozRefCountType aRefCount,
69 : const char* aTypeName, uint32_t aInstanceSize)
70 : {
71 7036 : MOZ_ASSERT(aRefCount != DEAD);
72 : NS_LogAddRef(const_cast<void*>(aPointer), aRefCount, aTypeName,
73 7036 : aInstanceSize);
74 7036 : }
75 :
76 5587 : static void logRelease(const void* aPointer, MozRefCountType aRefCount,
77 : const char* aTypeName)
78 : {
79 5587 : MOZ_ASSERT(aRefCount != DEAD);
80 5587 : NS_LogRelease(const_cast<void*>(aPointer), aRefCount, aTypeName);
81 5587 : }
82 : };
83 : #endif
84 :
85 : // This is used WeakPtr.h as well as this file.
86 : enum RefCountAtomicity
87 : {
88 : AtomicRefCount,
89 : NonAtomicRefCount
90 : };
91 :
92 : template<typename T, RefCountAtomicity Atomicity>
93 : class RefCounted
94 : {
95 : protected:
96 2474 : RefCounted() : mRefCnt(0) {}
97 1319 : ~RefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); }
98 :
99 : public:
100 : // Compatibility with nsRefPtr.
101 7036 : void AddRef() const
102 : {
103 : // Note: this method must be thread safe for AtomicRefCounted.
104 7036 : MOZ_ASSERT(int32_t(mRefCnt) >= 0);
105 : #ifndef MOZ_REFCOUNTED_LEAK_CHECKING
106 : ++mRefCnt;
107 : #else
108 7036 : const char* type = static_cast<const T*>(this)->typeName();
109 7036 : uint32_t size = static_cast<const T*>(this)->typeSize();
110 7036 : const void* ptr = static_cast<const T*>(this);
111 7036 : MozRefCountType cnt = ++mRefCnt;
112 7036 : detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
113 : #endif
114 7036 : }
115 :
116 5587 : void Release() const
117 : {
118 : // Note: this method must be thread safe for AtomicRefCounted.
119 5587 : MOZ_ASSERT(int32_t(mRefCnt) > 0);
120 : #ifndef MOZ_REFCOUNTED_LEAK_CHECKING
121 : MozRefCountType cnt = --mRefCnt;
122 : #else
123 5587 : const char* type = static_cast<const T*>(this)->typeName();
124 5587 : const void* ptr = static_cast<const T*>(this);
125 5587 : MozRefCountType cnt = --mRefCnt;
126 : // Note: it's not safe to touch |this| after decrementing the refcount,
127 : // except for below.
128 5587 : detail::RefCountLogger::logRelease(ptr, cnt, type);
129 : #endif
130 5587 : if (0 == cnt) {
131 : // Because we have atomically decremented the refcount above, only
132 : // one thread can get a 0 count here, so as long as we can assume that
133 : // everything else in the system is accessing this object through
134 : // RefPtrs, it's safe to access |this| here.
135 : #ifdef DEBUG
136 1319 : mRefCnt = detail::DEAD;
137 : #endif
138 1319 : delete static_cast<const T*>(this);
139 : }
140 5587 : }
141 :
142 : // Compatibility with wtf::RefPtr.
143 : void ref() { AddRef(); }
144 : void deref() { Release(); }
145 94 : MozRefCountType refCount() const { return mRefCnt; }
146 : bool hasOneRef() const
147 : {
148 : MOZ_ASSERT(mRefCnt > 0);
149 : return mRefCnt == 1;
150 : }
151 :
152 : private:
153 : mutable typename Conditional<Atomicity == AtomicRefCount,
154 : Atomic<MozRefCountType>,
155 : MozRefCountType>::Type mRefCnt;
156 : };
157 :
158 : #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
159 : // Passing override for the optional argument marks the typeName and
160 : // typeSize functions defined by this macro as overrides.
161 : #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...) \
162 : virtual const char* typeName() const __VA_ARGS__ { return #T; } \
163 : virtual size_t typeSize() const __VA_ARGS__ { return sizeof(*this); }
164 : #else
165 : #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...)
166 : #endif
167 :
168 : // Note that this macro is expanded unconditionally because it declares only
169 : // two small inline functions which will hopefully get eliminated by the linker
170 : // in non-leak-checking builds.
171 : #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \
172 : const char* typeName() const { return #T; } \
173 : size_t typeSize() const { return sizeof(*this); }
174 :
175 : } // namespace detail
176 :
177 : template<typename T>
178 2277 : class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount>
179 : {
180 : public:
181 1246 : ~RefCounted()
182 : {
183 : static_assert(IsBaseOf<RefCounted, T>::value,
184 : "T must derive from RefCounted<T>");
185 1246 : }
186 : };
187 :
188 : namespace external {
189 :
190 : /**
191 : * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
192 : * reference counter.
193 : *
194 : * NOTE: Please do not use this class, use NS_INLINE_DECL_THREADSAFE_REFCOUNTING
195 : * instead.
196 : */
197 : template<typename T>
198 197 : class AtomicRefCounted :
199 : public mozilla::detail::RefCounted<T, mozilla::detail::AtomicRefCount>
200 : {
201 : public:
202 73 : ~AtomicRefCounted()
203 : {
204 : static_assert(IsBaseOf<AtomicRefCounted, T>::value,
205 : "T must derive from AtomicRefCounted<T>");
206 73 : }
207 : };
208 :
209 : } // namespace external
210 :
211 : } // namespace mozilla
212 :
213 : #endif // mozilla_RefCounted_h
|