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 class RefCount { 106 public: 107 // Make gcc -Wnon-virtual-dtor happy. ~RefCount()108 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: RefCounted()121 RefCounted() : ref_count_(0) { 122 #if defined (ENABLE_OBJECT_COUNTER) 123 object_id_ = AtomicIncrement(&next_id_); 124 AtomicIncrement(&object_counter_); 125 DEBUG_OUTPUT("C "); 126 #endif 127 } RefCounted(const RefCounted<TDerived> &)128 RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {} ~RefCounted()129 virtual ~RefCounted() { 130 #if defined (ENABLE_OBJECT_COUNTER) 131 AtomicDecrement(&object_counter_); 132 DEBUG_OUTPUT("D "); 133 #endif 134 } 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: AddRef()142 virtual size_t AddRef() const { 143 size_t new_count = AtomicIncrement(&ref_count_); 144 DEBUG_OUTPUT("A "); 145 return new_count; 146 } 147 Release()148 virtual size_t Release() const { 149 size_t new_ref_count = AtomicDecrement(&ref_count_); 150 DEBUG_OUTPUT("R "); 151 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 delete (TDerived*)(this); 155 } 156 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: Ptr()176 Ptr() : p_(NULL) { 177 } 178 179 // This constructor shall not be explicit. 180 // lint does not like this but this is how it works. Ptr(T * pT)181 Ptr(T* pT) : p_(NULL) { 182 *this = pT; 183 } 184 Ptr(const Ptr<T> & p)185 Ptr(const Ptr<T>& p) : p_(NULL) { 186 *this = p; 187 } 188 ~Ptr()189 ~Ptr() { 190 Release(); 191 } 192 193 T* operator=(T* pT) { 194 if (p_ == pT) { 195 return p_; 196 } 197 if (pT) { 198 RefCount* p = static_cast<RefCount*>(pT); 199 if (p == NULL) { 200 return NULL; 201 } 202 p->AddRef(); // always AddRef() before Release() 203 } 204 Release(); 205 p_ = pT; 206 return p_; 207 } 208 209 T* operator=(const Ptr<T>& p) { 210 if (p_ == p.p_) { 211 return p_; 212 } 213 return operator=(p.p_); 214 } 215 216 operator T*&() { 217 return p_; 218 } 219 220 T& operator*() const { 221 return *p_; // It can throw! 222 } 223 224 T* operator->() const { 225 return p_; // It can throw! 226 } 227 228 bool operator!() const { 229 return (p_ == NULL); 230 } 231 232 bool operator<(const Ptr<T>& p) const { 233 return (p_ < p.p_); 234 } 235 236 bool operator!=(T* pT) const { 237 return !operator==(pT); 238 } 239 240 bool operator==(T* pT) const { 241 return (p_ == pT); 242 } 243 Release()244 size_t Release() const { 245 size_t ref_count = 0; 246 if (p_) { 247 RefCount* p = static_cast<RefCount*>(p_); 248 if (p) { 249 ref_count = p->Release(); 250 } 251 p_ = NULL; 252 } 253 return ref_count; 254 } 255 Attach(T * pT)256 void Attach(T* pT) { 257 if (p_ != pT) { 258 Release(); 259 p_ = pT; 260 } 261 } 262 Detach()263 T* Detach() { 264 T* pT = p_; 265 p_ = NULL; 266 return pT; 267 } 268 269 mutable T* p_; 270 }; 271 272 } // namespace sfntly 273 274 #endif // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_ 275