diff options
Diffstat (limited to 'sfntly/port/refcount.h')
-rw-r--r-- | sfntly/port/refcount.h | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/sfntly/port/refcount.h b/sfntly/port/refcount.h new file mode 100644 index 0000000..eed5162 --- /dev/null +++ b/sfntly/port/refcount.h @@ -0,0 +1,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_ |