/* * 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 { // public: // static Foo* CreateInstance() { // Ptr obj = new Foo(); // ref count = 1 // return obj.Detach(); // } // }; // typedef Ptr 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; // class B : public A, public RefCounted; // 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 specific implementation // class B : public I, public RefCounted; // 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 #include #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 class NoAddRefRelease : public T { public: NoAddRefRelease(); ~NoAddRefRelease(); private: virtual size_t AddRef() const = 0; virtual size_t Release() const = 0; }; template 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&) : ref_count_(0) {} virtual ~RefCounted() { #if defined (ENABLE_OBJECT_COUNTER) AtomicDecrement(&object_counter_); DEBUG_OUTPUT("D "); #endif } RefCounted& operator=(const RefCounted&) { // 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 size_t RefCounted::object_counter_ = 0; template size_t RefCounted::next_id_ = 0; #endif // semi-smart pointer for RefCount derived objects, similar to CComPtr template 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& p) : p_(NULL) { *this = p; } ~Ptr() { Release(); } T* operator=(T* pT) { if (p_ == pT) { return p_; } if (pT) { RefCount* p = static_cast(pT); if (p == NULL) { return NULL; } p->AddRef(); // always AddRef() before Release() } Release(); p_ = pT; return p_; } T* operator=(const Ptr& p) { if (p_ == p.p_) { return p_; } return operator=(p.p_); } operator T*&() { return p_; } T& operator*() const { return *p_; // It can throw! } NoAddRefRelease* operator->() const { return (NoAddRefRelease*)p_; // It can throw! } bool operator!() const { return (p_ == NULL); } bool operator<(const Ptr& 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(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_