blob: eed51622d7f702d4a9b43e55f3b1388fd93887c1 [file] [log] [blame] [edit]
/*
* 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_