• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 class RefCount {
103  public:
104   // Make gcc -Wnon-virtual-dtor happy.
~RefCount()105   virtual ~RefCount() {}
106 
107   virtual size_t AddRef() const = 0;
108   virtual size_t Release() const = 0;
109 };
110 
111 template <typename T>
112 class NoAddRefRelease : public T {
113  public:
114   NoAddRefRelease();
115   ~NoAddRefRelease();
116 
117  private:
118   virtual size_t AddRef() const = 0;
119   virtual size_t Release() const = 0;
120 };
121 
122 template <typename TDerived>
123 class RefCounted : virtual public RefCount {
124  public:
RefCounted()125   RefCounted() : ref_count_(0) {
126 #if defined (ENABLE_OBJECT_COUNTER)
127     object_id_ = AtomicIncrement(&next_id_);
128     AtomicIncrement(&object_counter_);
129     DEBUG_OUTPUT("C ");
130 #endif
131   }
RefCounted(const RefCounted<TDerived> &)132   RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
~RefCounted()133   virtual ~RefCounted() {
134 #if defined (ENABLE_OBJECT_COUNTER)
135     AtomicDecrement(&object_counter_);
136     DEBUG_OUTPUT("D ");
137 #endif
138   }
139 
140   RefCounted<TDerived>& operator=(const RefCounted<TDerived>&) {
141     // Each object maintains own ref count, don't propagate.
142     return *this;
143   }
144 
AddRef()145   virtual size_t AddRef() const {
146     size_t new_count = AtomicIncrement(&ref_count_);
147     DEBUG_OUTPUT("A ");
148     return new_count;
149   }
150 
Release()151   virtual size_t Release() const {
152     size_t new_ref_count = AtomicDecrement(&ref_count_);
153     DEBUG_OUTPUT("R ");
154     if (new_ref_count == 0) {
155       // A C-style is used to cast away const-ness and to derived.
156       // lint does not like this but this is how it works.
157       delete (TDerived*)(this);
158     }
159     return new_ref_count;
160   }
161 
162   mutable size_t ref_count_;  // reference count of current object
163 #if defined (ENABLE_OBJECT_COUNTER)
164   static size_t object_counter_;
165   static size_t next_id_;
166   mutable size_t object_id_;
167 #endif
168 };
169 
170 #if defined (ENABLE_OBJECT_COUNTER)
171 template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
172 template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
173 #endif
174 
175 // semi-smart pointer for RefCount derived objects, similar to CComPtr
176 template <typename T>
177 class Ptr {
178  public:
Ptr()179   Ptr() : p_(NULL) {
180   }
181 
182   // This constructor shall not be explicit.
183   // lint does not like this but this is how it works.
Ptr(T * pT)184   Ptr(T* pT) : p_(NULL) {
185     *this = pT;
186   }
187 
Ptr(const Ptr<T> & p)188   Ptr(const Ptr<T>& p) : p_(NULL) {
189     *this = p;
190   }
191 
~Ptr()192   ~Ptr() {
193     Release();
194   }
195 
196   T* operator=(T* pT) {
197     if (p_ == pT) {
198       return p_;
199     }
200     if (pT) {
201       RefCount* p = static_cast<RefCount*>(pT);
202       if (p == NULL) {
203         return NULL;
204       }
205       p->AddRef();  // always AddRef() before Release()
206     }
207     Release();
208     p_ = pT;
209     return p_;
210   }
211 
212   T* operator=(const Ptr<T>& p) {
213     if (p_ == p.p_) {
214       return p_;
215     }
216     return operator=(p.p_);
217   }
218 
219   operator T*&() {
220     return p_;
221   }
222 
223   T& operator*() const {
224     return *p_;  // It can throw!
225   }
226 
227   NoAddRefRelease<T>* operator->() const {
228     return (NoAddRefRelease<T>*)p_;  // It can throw!
229   }
230 
231   bool operator!() const {
232     return (p_ == NULL);
233   }
234 
235   bool operator<(const Ptr<T>& p) const {
236     return (p_ < p.p_);
237   }
238 
239   bool operator!=(T* pT) const {
240     return !operator==(pT);
241   }
242 
243   bool operator==(T* pT) const {
244     return (p_ == pT);
245   }
246 
Release()247   size_t Release() const {
248     size_t ref_count = 0;
249     if (p_) {
250       RefCount* p = static_cast<RefCount*>(p_);
251       if (p) {
252         ref_count = p->Release();
253       }
254       p_ = NULL;
255     }
256     return ref_count;
257   }
258 
Attach(T * pT)259   void Attach(T* pT) {
260     if (p_ != pT) {
261       Release();
262       p_ = pT;
263     }
264   }
265 
Detach()266   T* Detach() {
267     T* pT = p_;
268     p_ = NULL;
269     return pT;
270   }
271 
272   mutable T* p_;
273 };
274 
275 }  // namespace sfntly
276 
277 #endif  // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
278