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
100namespace sfntly {
101
102class RefCount {
103 public:
104  // Make gcc -Wnon-virtual-dtor happy.
105  virtual ~RefCount() {}
106
107  virtual size_t AddRef() const = 0;
108  virtual size_t Release() const = 0;
109};
110
111template <typename T>
112class 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
122template <typename TDerived>
123class RefCounted : virtual public RefCount {
124 public:
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  }
132  RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
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
145  virtual size_t AddRef() const {
146    size_t new_count = AtomicIncrement(&ref_count_);
147    DEBUG_OUTPUT("A ");
148    return new_count;
149  }
150
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)
171template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
172template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
173#endif
174
175// semi-smart pointer for RefCount derived objects, similar to CComPtr
176template <typename T>
177class Ptr {
178 public:
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.
184  Ptr(T* pT) : p_(NULL) {
185    *this = pT;
186  }
187
188  Ptr(const Ptr<T>& p) : p_(NULL) {
189    *this = p;
190  }
191
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
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
259  void Attach(T* pT) {
260    if (p_ != pT) {
261      Release();
262      p_ = pT;
263    }
264  }
265
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