Line data Source code
1 : /*
2 : * Copyright 2011 Google Inc. All Rights Reserved.
3 : *
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 :
17 : // Object reference count and smart pointer implementation.
18 :
19 : // Smart pointer usage in sfntly:
20 : //
21 : // sfntly carries a smart pointer implementation like COM. Ref-countable object
22 : // type inherits from RefCounted<>, which have AddRef and Release just like
23 : // IUnknown (but no QueryInterface). Use a Ptr<> based smart pointer to hold
24 : // the object so that the object ref count is handled correctly.
25 : //
26 : // class Foo : public RefCounted<Foo> {
27 : // public:
28 : // static Foo* CreateInstance() {
29 : // Ptr<Foo> obj = new Foo(); // ref count = 1
30 : // return obj.Detach();
31 : // }
32 : // };
33 : // typedef Ptr<Foo> FooPtr; // common short-hand notation
34 : // FooPtr obj;
35 : // obj.Attach(Foo::CreatedInstance()); // ref count = 1
36 : // {
37 : // FooPtr obj2 = obj; // ref count = 2
38 : // } // ref count = 1, obj2 out of scope
39 : // obj.Release(); // ref count = 0, object destroyed
40 :
41 : // Notes on usage:
42 : // 1. Virtual inherit from RefCount interface in base class if smart pointers
43 : // are going to be defined.
44 : // 2. All RefCounted objects must be instantiated on the heap. Allocating the
45 : // object on stack will cause crash.
46 : // 3. Be careful when you have complex inheritance. For example,
47 : // class A : public RefCounted<A>;
48 : // class B : public A, public RefCounted<B>;
49 : // In this case the smart pointer is pretty dumb and don't count on it to
50 : // nicely destroy your objects as designed. Try refactor your code like
51 : // class I; // the common interface and implementations
52 : // class A : public I, public RefCounted<A>; // A specific implementation
53 : // class B : public I, public RefCounted<B>; // B specific implementation
54 : // 4. Smart pointers here are very bad candidates for function parameters. Use
55 : // dumb pointers in function parameter list.
56 : // 5. When down_cast is performed on a dangling pointer due to bugs in code,
57 : // VC++ will generate SEH which is not handled well in VC++ debugger. One
58 : // can use WinDBG to run it and get the faulting stack.
59 : // 6. Idioms for heap object as return value
60 : // Foo* createFoo() { FooPtr obj = new Foo(); return obj.Detach(); }
61 : // Foo* passthru() { FooPtr obj = createFoo(), return obj; }
62 : // FooPtr end_scope_pointer;
63 : // end_scope_pointer.Attach(passThrough);
64 : // If you are not passing that object back, you are the end of scope.
65 :
66 : #ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
67 : #define SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
68 :
69 : #if !defined (NDEBUG)
70 : #define ENABLE_OBJECT_COUNTER
71 : // #define REF_COUNT_DEBUGGING
72 : #endif
73 :
74 : #if defined (REF_COUNT_DEBUGGING)
75 : #include <stdio.h>
76 : #include <typeinfo>
77 : #endif
78 :
79 : #include "sfntly/port/atomic.h"
80 : #include "sfntly/port/type.h"
81 :
82 : // Special tag for functions that requires caller to attach instead of using
83 : // assignment operators.
84 : #define CALLER_ATTACH
85 :
86 : #if defined (REF_COUNT_DEBUGGING)
87 : #define DEBUG_OUTPUT(a) \
88 : fprintf(stderr, "%s%s:oc=%d,oid=%d,rc=%d\n", a, \
89 : typeid(this).name(), object_counter_, object_id_, ref_count_)
90 : #else
91 : #define DEBUG_OUTPUT(a)
92 : #endif
93 :
94 : #if defined (_MSC_VER)
95 : // VC 2008/2010 incorrectly gives this warning for pure virtual functions
96 : // in virtual inheritance. The only way to get around it is to disable it.
97 : #pragma warning(disable:4250)
98 : #endif
99 :
100 : namespace sfntly {
101 :
102 : template <typename T>
103 : class Ptr;
104 :
105 0 : class RefCount {
106 : public:
107 : // Make gcc -Wnon-virtual-dtor happy.
108 0 : virtual ~RefCount() {}
109 :
110 : private:
111 : template <typename T>
112 : friend class Ptr;
113 :
114 : virtual size_t AddRef() const = 0;
115 : virtual size_t Release() const = 0;
116 : };
117 :
118 : template <typename TDerived>
119 : class RefCounted : virtual public RefCount {
120 : public:
121 0 : RefCounted() : ref_count_(0) {
122 : #if defined (ENABLE_OBJECT_COUNTER)
123 0 : object_id_ = AtomicIncrement(&next_id_);
124 0 : AtomicIncrement(&object_counter_);
125 : DEBUG_OUTPUT("C ");
126 : #endif
127 0 : }
128 : RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
129 0 : virtual ~RefCounted() {
130 : #if defined (ENABLE_OBJECT_COUNTER)
131 0 : AtomicDecrement(&object_counter_);
132 : DEBUG_OUTPUT("D ");
133 : #endif
134 0 : }
135 :
136 : RefCounted<TDerived>& operator=(const RefCounted<TDerived>&) {
137 : // Each object maintains own ref count, don't propagate.
138 : return *this;
139 : }
140 :
141 : private:
142 0 : virtual size_t AddRef() const {
143 0 : size_t new_count = AtomicIncrement(&ref_count_);
144 : DEBUG_OUTPUT("A ");
145 0 : return new_count;
146 : }
147 :
148 0 : virtual size_t Release() const {
149 0 : size_t new_ref_count = AtomicDecrement(&ref_count_);
150 : DEBUG_OUTPUT("R ");
151 0 : if (new_ref_count == 0) {
152 : // A C-style is used to cast away const-ness and to derived.
153 : // lint does not like this but this is how it works.
154 0 : delete (TDerived*)(this);
155 : }
156 0 : return new_ref_count;
157 : }
158 :
159 : mutable size_t ref_count_; // reference count of current object
160 : #if defined (ENABLE_OBJECT_COUNTER)
161 : static size_t object_counter_;
162 : static size_t next_id_;
163 : mutable size_t object_id_;
164 : #endif
165 : };
166 :
167 : #if defined (ENABLE_OBJECT_COUNTER)
168 : template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
169 : template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
170 : #endif
171 :
172 : // semi-smart pointer for RefCount derived objects, similar to CComPtr
173 : template <typename T>
174 : class Ptr {
175 : public:
176 0 : Ptr() : p_(NULL) {
177 0 : }
178 :
179 : // This constructor shall not be explicit.
180 : // lint does not like this but this is how it works.
181 0 : Ptr(T* pT) : p_(NULL) {
182 0 : *this = pT;
183 0 : }
184 :
185 0 : Ptr(const Ptr<T>& p) : p_(NULL) {
186 0 : *this = p;
187 0 : }
188 :
189 0 : ~Ptr() {
190 0 : Release();
191 0 : }
192 :
193 0 : T* operator=(T* pT) {
194 0 : if (p_ == pT) {
195 0 : return p_;
196 : }
197 0 : if (pT) {
198 0 : RefCount* p = static_cast<RefCount*>(pT);
199 0 : if (p == NULL) {
200 0 : return NULL;
201 : }
202 0 : p->AddRef(); // always AddRef() before Release()
203 : }
204 0 : Release();
205 0 : p_ = pT;
206 0 : return p_;
207 : }
208 :
209 0 : T* operator=(const Ptr<T>& p) {
210 0 : if (p_ == p.p_) {
211 0 : return p_;
212 : }
213 0 : return operator=(p.p_);
214 : }
215 :
216 0 : operator T*&() {
217 0 : return p_;
218 : }
219 :
220 : T& operator*() const {
221 : return *p_; // It can throw!
222 : }
223 :
224 0 : T* operator->() const {
225 0 : return p_; // It can throw!
226 : }
227 :
228 0 : bool operator!() const {
229 0 : return (p_ == NULL);
230 : }
231 :
232 0 : bool operator<(const Ptr<T>& p) const {
233 0 : return (p_ < p.p_);
234 : }
235 :
236 0 : bool operator!=(T* pT) const {
237 0 : return !operator==(pT);
238 : }
239 :
240 0 : bool operator==(T* pT) const {
241 0 : return (p_ == pT);
242 : }
243 :
244 0 : size_t Release() const {
245 0 : size_t ref_count = 0;
246 0 : if (p_) {
247 0 : RefCount* p = static_cast<RefCount*>(p_);
248 0 : if (p) {
249 0 : ref_count = p->Release();
250 : }
251 0 : p_ = NULL;
252 : }
253 0 : return ref_count;
254 : }
255 :
256 0 : void Attach(T* pT) {
257 0 : if (p_ != pT) {
258 0 : Release();
259 0 : p_ = pT;
260 : }
261 0 : }
262 :
263 0 : T* Detach() {
264 0 : T* pT = p_;
265 0 : p_ = NULL;
266 0 : return pT;
267 : }
268 :
269 : mutable T* p_;
270 : };
271 :
272 : } // namespace sfntly
273 :
274 : #endif // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
|