summaryrefslogtreecommitdiff
path: root/sfntly/port/refcount.h
blob: eed51622d7f702d4a9b43e55f3b1388fd93887c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Object reference count and smart pointer implementation.

// Smart pointer usage in sfntly:
//
// sfntly carries a smart pointer implementation like COM.  Ref-countable object
// type inherits from RefCounted<>, which have AddRef and Release just like
// IUnknown (but no QueryInterface).  Use a Ptr<> based smart pointer to hold
// the object so that the object ref count is handled correctly.
//
// class Foo : public RefCounted<Foo> {
//  public:
//   static Foo* CreateInstance() {
//     Ptr<Foo> obj = new Foo();  // ref count = 1
//     return obj.Detach();
//   }
// };
// typedef Ptr<Foo> FooPtr;  // common short-hand notation
// FooPtr obj;
// obj.Attach(Foo::CreatedInstance());  // ref count = 1
// {
//   FooPtr obj2 = obj;  // ref count = 2
// }  // ref count = 1, obj2 out of scope
// obj.Release();  // ref count = 0, object destroyed

// Notes on usage:
// 1. Virtual inherit from RefCount interface in base class if smart pointers
//    are going to be defined.
// 2. All RefCounted objects must be instantiated on the heap.  Allocating the
//    object on stack will cause crash.
// 3. Be careful when you have complex inheritance.  For example,
//    class A : public RefCounted<A>;
//    class B : public A, public RefCounted<B>;
//    In this case the smart pointer is pretty dumb and don't count on it to
//    nicely destroy your objects as designed. Try refactor your code like
//    class I;  // the common interface and implementations
//    class A : public I, public RefCounted<A>;  // A specific implementation
//    class B : public I, public RefCounted<B>;  // B specific implementation
// 4. Smart pointers here are very bad candidates for function parameters.  Use
//    dumb pointers in function parameter list.
// 5. When down_cast is performed on a dangling pointer due to bugs in code,
//    VC++ will generate SEH which is not handled well in VC++ debugger.  One
//    can use WinDBG to run it and get the faulting stack.
// 6. Idioms for heap object as return value
//    Foo* createFoo() { FooPtr obj = new Foo(); return obj.Detach(); }
//    Foo* passthru() { FooPtr obj = createFoo(), return obj; }
//    FooPtr end_scope_pointer;
//    end_scope_pointer.Attach(passThrough);
//    If you are not passing that object back, you are the end of scope.

#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
#define SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_

#if !defined (NDEBUG)
  #define ENABLE_OBJECT_COUNTER
//  #define REF_COUNT_DEBUGGING
#endif

#if defined (REF_COUNT_DEBUGGING)
  #include <stdio.h>
  #include <typeinfo>
#endif

#include "sfntly/port/atomic.h"
#include "sfntly/port/type.h"

// Special tag for functions that requires caller to attach instead of using
// assignment operators.
#define CALLER_ATTACH

#if defined (REF_COUNT_DEBUGGING)
  #define DEBUG_OUTPUT(a) \
      fprintf(stderr, "%s%s:oc=%d,oid=%d,rc=%d\n", a, \
              typeid(this).name(), object_counter_, object_id_, ref_count_)
#else
  #define DEBUG_OUTPUT(a)
#endif

#if defined (_MSC_VER)
  // VC 2008/2010 incorrectly gives this warning for pure virtual functions
  // in virtual inheritance.  The only way to get around it is to disable it.
  #pragma warning(disable:4250)
#endif

namespace sfntly {

class RefCount {
 public:
  // Make gcc -Wnon-virtual-dtor happy.
  virtual ~RefCount() {}

  virtual size_t AddRef() const = 0;
  virtual size_t Release() const = 0;
};

template <typename T>
class NoAddRefRelease : public T {
 public:
  NoAddRefRelease();
  ~NoAddRefRelease();

 private:
  virtual size_t AddRef() const = 0;
  virtual size_t Release() const = 0;
};

template <typename TDerived>
class RefCounted : virtual public RefCount {
 public:
  RefCounted() : ref_count_(0) {
#if defined (ENABLE_OBJECT_COUNTER)
    object_id_ = AtomicIncrement(&next_id_);
    AtomicIncrement(&object_counter_);
    DEBUG_OUTPUT("C ");
#endif
  }
  RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
  virtual ~RefCounted() {
#if defined (ENABLE_OBJECT_COUNTER)
    AtomicDecrement(&object_counter_);
    DEBUG_OUTPUT("D ");
#endif
  }

  RefCounted<TDerived>& operator=(const RefCounted<TDerived>&) {
    // Each object maintains own ref count, don't propagate.
    return *this;
  }

  virtual size_t AddRef() const {
    size_t new_count = AtomicIncrement(&ref_count_);
    DEBUG_OUTPUT("A ");
    return new_count;
  }

  virtual size_t Release() const {
    size_t new_ref_count = AtomicDecrement(&ref_count_);
    DEBUG_OUTPUT("R ");
    if (new_ref_count == 0) {
      // A C-style is used to cast away const-ness and to derived.
      // lint does not like this but this is how it works.
      delete (TDerived*)(this);
    }
    return new_ref_count;
  }

  mutable size_t ref_count_;  // reference count of current object
#if defined (ENABLE_OBJECT_COUNTER)
  static size_t object_counter_;
  static size_t next_id_;
  mutable size_t object_id_;
#endif
};

#if defined (ENABLE_OBJECT_COUNTER)
template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
#endif

// semi-smart pointer for RefCount derived objects, similar to CComPtr
template <typename T>
class Ptr {
 public:
  Ptr() : p_(NULL) {
  }

  // This constructor shall not be explicit.
  // lint does not like this but this is how it works.
  Ptr(T* pT) : p_(NULL) {
    *this = pT;
  }

  Ptr(const Ptr<T>& p) : p_(NULL) {
    *this = p;
  }

  ~Ptr() {
    Release();
  }

  T* operator=(T* pT) {
    if (p_ == pT) {
      return p_;
    }
    if (pT) {
      RefCount* p = static_cast<RefCount*>(pT);
      if (p == NULL) {
        return NULL;
      }
      p->AddRef();  // always AddRef() before Release()
    }
    Release();
    p_ = pT;
    return p_;
  }

  T* operator=(const Ptr<T>& p) {
    if (p_ == p.p_) {
      return p_;
    }
    return operator=(p.p_);
  }

  operator T*&() {
    return p_;
  }

  T& operator*() const {
    return *p_;  // It can throw!
  }

  NoAddRefRelease<T>* operator->() const {
    return (NoAddRefRelease<T>*)p_;  // It can throw!
  }

  bool operator!() const {
    return (p_ == NULL);
  }

  bool operator<(const Ptr<T>& p) const {
    return (p_ < p.p_);
  }

  bool operator!=(T* pT) const {
    return !operator==(pT);
  }

  bool operator==(T* pT) const {
    return (p_ == pT);
  }

  size_t Release() const {
    size_t ref_count = 0;
    if (p_) {
      RefCount* p = static_cast<RefCount*>(p_);
      if (p) {
        ref_count = p->Release();
      }
      p_ = NULL;
    }
    return ref_count;
  }

  void Attach(T* pT) {
    if (p_ != pT) {
      Release();
      p_ = pT;
    }
  }

  T* Detach() {
    T* pT = p_;
    p_ = NULL;
    return pT;
  }

  mutable T* p_;
};

}  // namespace sfntly

#endif  // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_