Home | History | Annotate | Download | only in port
      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.
    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:
    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   }
    128   RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
    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:
    142   virtual size_t AddRef() const {
    143     size_t new_count = AtomicIncrement(&ref_count_);
    144     DEBUG_OUTPUT("A ");
    145     return new_count;
    146   }
    147 
    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:
    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.
    181   Ptr(T* pT) : p_(NULL) {
    182     *this = pT;
    183   }
    184 
    185   Ptr(const Ptr<T>& p) : p_(NULL) {
    186     *this = p;
    187   }
    188 
    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 
    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 
    256   void Attach(T* pT) {
    257     if (p_ != pT) {
    258       Release();
    259       p_ = pT;
    260     }
    261   }
    262 
    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