michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Helpers for defining and using refcounted objects. */ michael@0: michael@0: #ifndef mozilla_RefPtr_h michael@0: #define mozilla_RefPtr_h michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/RefCountType.h" michael@0: #include "mozilla/TypeTraits.h" michael@0: #if defined(MOZILLA_INTERNAL_API) michael@0: #include "nsXPCOM.h" michael@0: #endif michael@0: michael@0: #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) michael@0: #define MOZ_REFCOUNTED_LEAK_CHECKING michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: template class RefCounted; michael@0: template class RefPtr; michael@0: template class TemporaryRef; michael@0: template class OutParamRef; michael@0: template OutParamRef byRef(RefPtr&); michael@0: michael@0: /** michael@0: * RefCounted is a sort of a "mixin" for a class T. RefCounted michael@0: * manages, well, refcounting for T, and because RefCounted is michael@0: * parameterized on T, RefCounted can call T's destructor directly. michael@0: * This means T doesn't need to have a virtual dtor and so doesn't michael@0: * need a vtable. michael@0: * michael@0: * RefCounted is created with refcount == 0. Newly-allocated michael@0: * RefCounted must immediately be assigned to a RefPtr to make the michael@0: * refcount > 0. It's an error to allocate and free a bare michael@0: * RefCounted, i.e. outside of the RefPtr machinery. Attempts to michael@0: * do so will abort DEBUG builds. michael@0: * michael@0: * Live RefCounted have refcount > 0. The lifetime (refcounts) of michael@0: * live RefCounted are controlled by RefPtr and michael@0: * RefPtr. Upon a transition from refcounted==1 michael@0: * to 0, the RefCounted "dies" and is destroyed. The "destroyed" michael@0: * state is represented in DEBUG builds by refcount==0xffffdead. This michael@0: * state distinguishes use-before-ref (refcount==0) from michael@0: * use-after-destroy (refcount==0xffffdead). michael@0: * michael@0: * Note that when deriving from RefCounted or AtomicRefCounted, you michael@0: * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public michael@0: * section of your class, where ClassName is the name of your class. michael@0: */ michael@0: namespace detail { michael@0: #ifdef DEBUG michael@0: const MozRefCountType DEAD = 0xffffdead; michael@0: #endif michael@0: michael@0: // When building code that gets compiled into Gecko, try to use the michael@0: // trace-refcount leak logging facilities. michael@0: #ifdef MOZ_REFCOUNTED_LEAK_CHECKING michael@0: class RefCountLogger michael@0: { michael@0: public: michael@0: static void logAddRef(const void* aPointer, MozRefCountType aRefCount, michael@0: const char* aTypeName, uint32_t aInstanceSize) michael@0: { michael@0: MOZ_ASSERT(aRefCount != DEAD); michael@0: NS_LogAddRef(const_cast(aPointer), aRefCount, aTypeName, aInstanceSize); michael@0: } michael@0: michael@0: static void logRelease(const void* aPointer, MozRefCountType aRefCount, michael@0: const char* aTypeName) michael@0: { michael@0: MOZ_ASSERT(aRefCount != DEAD); michael@0: NS_LogRelease(const_cast(aPointer), aRefCount, aTypeName); michael@0: } michael@0: }; michael@0: #endif michael@0: michael@0: // This is used WeakPtr.h as well as this file. michael@0: enum RefCountAtomicity michael@0: { michael@0: AtomicRefCount, michael@0: NonAtomicRefCount michael@0: }; michael@0: michael@0: template michael@0: class RefCounted michael@0: { michael@0: friend class RefPtr; michael@0: michael@0: protected: michael@0: RefCounted() : refCnt(0) { } michael@0: ~RefCounted() { michael@0: MOZ_ASSERT(refCnt == detail::DEAD); michael@0: } michael@0: michael@0: public: michael@0: // Compatibility with nsRefPtr. michael@0: void AddRef() const { michael@0: // Note: this method must be thread safe for AtomicRefCounted. michael@0: MOZ_ASSERT(int32_t(refCnt) >= 0); michael@0: #ifndef MOZ_REFCOUNTED_LEAK_CHECKING michael@0: ++refCnt; michael@0: #else michael@0: const char* type = static_cast(this)->typeName(); michael@0: uint32_t size = static_cast(this)->typeSize(); michael@0: const void* ptr = static_cast(this); michael@0: MozRefCountType cnt = ++refCnt; michael@0: detail::RefCountLogger::logAddRef(ptr, cnt, type, size); michael@0: #endif michael@0: } michael@0: michael@0: void Release() const { michael@0: // Note: this method must be thread safe for AtomicRefCounted. michael@0: MOZ_ASSERT(int32_t(refCnt) > 0); michael@0: #ifndef MOZ_REFCOUNTED_LEAK_CHECKING michael@0: MozRefCountType cnt = --refCnt; michael@0: #else michael@0: const char* type = static_cast(this)->typeName(); michael@0: const void* ptr = static_cast(this); michael@0: MozRefCountType cnt = --refCnt; michael@0: // Note: it's not safe to touch |this| after decrementing the refcount, michael@0: // except for below. michael@0: detail::RefCountLogger::logRelease(ptr, cnt, type); michael@0: #endif michael@0: if (0 == cnt) { michael@0: // Because we have atomically decremented the refcount above, only michael@0: // one thread can get a 0 count here, so as long as we can assume that michael@0: // everything else in the system is accessing this object through michael@0: // RefPtrs, it's safe to access |this| here. michael@0: #ifdef DEBUG michael@0: refCnt = detail::DEAD; michael@0: #endif michael@0: delete static_cast(this); michael@0: } michael@0: } michael@0: michael@0: // Compatibility with wtf::RefPtr. michael@0: void ref() { AddRef(); } michael@0: void deref() { Release(); } michael@0: MozRefCountType refCount() const { return refCnt; } michael@0: bool hasOneRef() const { michael@0: MOZ_ASSERT(refCnt > 0); michael@0: return refCnt == 1; michael@0: } michael@0: michael@0: private: michael@0: mutable typename Conditional, MozRefCountType>::Type refCnt; michael@0: }; michael@0: michael@0: #ifdef MOZ_REFCOUNTED_LEAK_CHECKING michael@0: #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \ michael@0: const char* typeName() const { return #T; } \ michael@0: size_t typeSize() const { return sizeof(*this); } michael@0: michael@0: #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T) \ michael@0: virtual const char* typeName() const { return #T; } \ michael@0: virtual size_t typeSize() const { return sizeof(*this); } michael@0: #else michael@0: #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) michael@0: #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T) michael@0: #endif michael@0: michael@0: } michael@0: michael@0: template michael@0: class RefCounted : public detail::RefCounted michael@0: { michael@0: public: michael@0: ~RefCounted() { michael@0: static_assert(IsBaseOf::value, michael@0: "T must derive from RefCounted"); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * AtomicRefCounted is like RefCounted, with an atomically updated michael@0: * reference counter. michael@0: */ michael@0: template michael@0: class AtomicRefCounted : public detail::RefCounted michael@0: { michael@0: public: michael@0: ~AtomicRefCounted() { michael@0: static_assert(IsBaseOf::value, michael@0: "T must derive from AtomicRefCounted"); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * RefPtr points to a refcounted thing that has AddRef and Release michael@0: * methods to increase/decrease the refcount, respectively. After a michael@0: * RefPtr is assigned a T*, the T* can be used through the RefPtr michael@0: * as if it were a T*. michael@0: * michael@0: * A RefPtr can forget its underlying T*, which results in the T* michael@0: * being wrapped in a temporary object until the T* is either michael@0: * re-adopted from or released by the temporary. michael@0: */ michael@0: template michael@0: class RefPtr michael@0: { michael@0: // To allow them to use unref() michael@0: friend class TemporaryRef; michael@0: friend class OutParamRef; michael@0: michael@0: struct DontRef {}; michael@0: michael@0: public: michael@0: RefPtr() : ptr(0) { } michael@0: RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {} michael@0: RefPtr(const TemporaryRef& o) : ptr(o.drop()) {} michael@0: RefPtr(T* t) : ptr(ref(t)) {} michael@0: michael@0: template michael@0: RefPtr(const RefPtr& o) : ptr(ref(o.get())) {} michael@0: michael@0: ~RefPtr() { unref(ptr); } michael@0: michael@0: RefPtr& operator=(const RefPtr& o) { michael@0: assign(ref(o.ptr)); michael@0: return *this; michael@0: } michael@0: RefPtr& operator=(const TemporaryRef& o) { michael@0: assign(o.drop()); michael@0: return *this; michael@0: } michael@0: RefPtr& operator=(T* t) { michael@0: assign(ref(t)); michael@0: return *this; michael@0: } michael@0: michael@0: template michael@0: RefPtr& operator=(const RefPtr& o) { michael@0: assign(ref(o.get())); michael@0: return *this; michael@0: } michael@0: michael@0: TemporaryRef forget() { michael@0: T* tmp = ptr; michael@0: ptr = 0; michael@0: return TemporaryRef(tmp, DontRef()); michael@0: } michael@0: michael@0: T* get() const { return ptr; } michael@0: operator T*() const { return ptr; } michael@0: T* operator->() const { return ptr; } michael@0: T& operator*() const { return *ptr; } michael@0: template michael@0: operator TemporaryRef() { return TemporaryRef(ptr); } michael@0: michael@0: private: michael@0: void assign(T* t) { michael@0: unref(ptr); michael@0: ptr = t; michael@0: } michael@0: michael@0: T* ptr; michael@0: michael@0: static MOZ_ALWAYS_INLINE T* ref(T* t) { michael@0: if (t) michael@0: t->AddRef(); michael@0: return t; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE void unref(T* t) { michael@0: if (t) michael@0: t->Release(); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * TemporaryRef represents an object that holds a temporary michael@0: * reference to a T. TemporaryRef objects can't be manually ref'd or michael@0: * unref'd (being temporaries, not lvalues), so can only relinquish michael@0: * references to other objects, or unref on destruction. michael@0: */ michael@0: template michael@0: class TemporaryRef michael@0: { michael@0: // To allow it to construct TemporaryRef from a bare T* michael@0: friend class RefPtr; michael@0: michael@0: typedef typename RefPtr::DontRef DontRef; michael@0: michael@0: public: michael@0: TemporaryRef(T* t) : ptr(RefPtr::ref(t)) {} michael@0: TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} michael@0: michael@0: template michael@0: TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} michael@0: michael@0: ~TemporaryRef() { RefPtr::unref(ptr); } michael@0: michael@0: T* drop() const { michael@0: T* tmp = ptr; michael@0: ptr = 0; michael@0: return tmp; michael@0: } michael@0: michael@0: private: michael@0: TemporaryRef(T* t, const DontRef&) : ptr(t) {} michael@0: michael@0: mutable T* ptr; michael@0: michael@0: TemporaryRef() MOZ_DELETE; michael@0: void operator=(const TemporaryRef&) MOZ_DELETE; michael@0: }; michael@0: michael@0: /** michael@0: * OutParamRef is a wrapper that tracks a refcounted pointer passed as michael@0: * an outparam argument to a function. OutParamRef implements COM T** michael@0: * outparam semantics: this requires the callee to AddRef() the T* michael@0: * returned through the T** outparam on behalf of the caller. This michael@0: * means the caller (through OutParamRef) must Release() the old michael@0: * object contained in the tracked RefPtr. It's OK if the callee michael@0: * returns the same T* passed to it through the T** outparam, as long michael@0: * as the callee obeys the COM discipline. michael@0: * michael@0: * Prefer returning TemporaryRef from functions over creating T** michael@0: * outparams and passing OutParamRef to T**. Prefer RefPtr* michael@0: * outparams over T** outparams. michael@0: */ michael@0: template michael@0: class OutParamRef michael@0: { michael@0: friend OutParamRef byRef(RefPtr&); michael@0: michael@0: public: michael@0: ~OutParamRef() { michael@0: RefPtr::unref(refPtr.ptr); michael@0: refPtr.ptr = tmp; michael@0: } michael@0: michael@0: operator T**() { return &tmp; } michael@0: michael@0: private: michael@0: OutParamRef(RefPtr& p) : refPtr(p), tmp(p.get()) {} michael@0: michael@0: RefPtr& refPtr; michael@0: T* tmp; michael@0: michael@0: OutParamRef() MOZ_DELETE; michael@0: OutParamRef& operator=(const OutParamRef&) MOZ_DELETE; michael@0: }; michael@0: michael@0: /** michael@0: * byRef cooperates with OutParamRef to implement COM outparam semantics. michael@0: */ michael@0: template michael@0: OutParamRef michael@0: byRef(RefPtr& ptr) michael@0: { michael@0: return OutParamRef(ptr); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #if 0 michael@0: michael@0: // Command line that builds these tests michael@0: // michael@0: // cp RefPtr.h test.cc && g++ -g -Wall -pedantic -DDEBUG -o test test.cc && ./test michael@0: michael@0: using namespace mozilla; michael@0: michael@0: struct Foo : public RefCounted michael@0: { michael@0: MOZ_DECLARE_REFCOUNTED_TYPENAME(Foo) michael@0: Foo() : dead(false) { } michael@0: ~Foo() { michael@0: MOZ_ASSERT(!dead); michael@0: dead = true; michael@0: numDestroyed++; michael@0: } michael@0: michael@0: bool dead; michael@0: static int numDestroyed; michael@0: }; michael@0: int Foo::numDestroyed; michael@0: michael@0: struct Bar : public Foo { }; michael@0: michael@0: TemporaryRef michael@0: NewFoo() michael@0: { michael@0: return RefPtr(new Foo()); michael@0: } michael@0: michael@0: TemporaryRef michael@0: NewBar() michael@0: { michael@0: return new Bar(); michael@0: } michael@0: michael@0: void michael@0: GetNewFoo(Foo** f) michael@0: { michael@0: *f = new Bar(); michael@0: // Kids, don't try this at home michael@0: (*f)->AddRef(); michael@0: } michael@0: michael@0: void michael@0: GetPassedFoo(Foo** f) michael@0: { michael@0: // Kids, don't try this at home michael@0: (*f)->AddRef(); michael@0: } michael@0: michael@0: void michael@0: GetNewFoo(RefPtr* f) michael@0: { michael@0: *f = new Bar(); michael@0: } michael@0: michael@0: void michael@0: GetPassedFoo(RefPtr* f) michael@0: {} michael@0: michael@0: TemporaryRef michael@0: GetNullFoo() michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: int michael@0: main(int argc, char** argv) michael@0: { michael@0: // This should blow up michael@0: // Foo* f = new Foo(); delete f; michael@0: michael@0: MOZ_ASSERT(0 == Foo::numDestroyed); michael@0: { michael@0: RefPtr f = new Foo(); michael@0: MOZ_ASSERT(f->refCount() == 1); michael@0: } michael@0: MOZ_ASSERT(1 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f1 = NewFoo(); michael@0: RefPtr f2(NewFoo()); michael@0: MOZ_ASSERT(1 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(3 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr b = NewBar(); michael@0: MOZ_ASSERT(3 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(4 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f1; michael@0: { michael@0: f1 = new Foo(); michael@0: RefPtr f2(f1); michael@0: RefPtr f3 = f2; michael@0: MOZ_ASSERT(4 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(4 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(5 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f = new Foo(); michael@0: f.forget(); michael@0: MOZ_ASSERT(6 == Foo::numDestroyed); michael@0: } michael@0: michael@0: { michael@0: RefPtr f = new Foo(); michael@0: GetNewFoo(byRef(f)); michael@0: MOZ_ASSERT(7 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(8 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f = new Foo(); michael@0: GetPassedFoo(byRef(f)); michael@0: MOZ_ASSERT(8 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(9 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f = new Foo(); michael@0: GetNewFoo(&f); michael@0: MOZ_ASSERT(10 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(11 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f = new Foo(); michael@0: GetPassedFoo(&f); michael@0: MOZ_ASSERT(11 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(12 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f1 = new Bar(); michael@0: } michael@0: MOZ_ASSERT(13 == Foo::numDestroyed); michael@0: michael@0: { michael@0: RefPtr f = GetNullFoo(); michael@0: MOZ_ASSERT(13 == Foo::numDestroyed); michael@0: } michael@0: MOZ_ASSERT(13 == Foo::numDestroyed); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #endif /* mozilla_RefPtr_h */