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