diff --git a/gfx/layers/AtomicRefCountedWithFinalize.h b/gfx/layers/AtomicRefCountedWithFinalize.h index aef847d4a717..4c7c3e94c28c 100644 --- a/gfx/layers/AtomicRefCountedWithFinalize.h +++ b/gfx/layers/AtomicRefCountedWithFinalize.h @@ -62,6 +62,9 @@ protected: public: // Mark user classes that are considered flawless. + template + friend class RefPtr; + template friend class ::mozilla::StaticRefPtr; diff --git a/mfbt/RefCounted.h b/mfbt/RefCounted.h index 68069a513f07..5532cffc4dcb 100644 --- a/mfbt/RefCounted.h +++ b/mfbt/RefCounted.h @@ -29,6 +29,8 @@ namespace mozilla { +template class RefPtr; + /** * RefCounted is a sort of a "mixin" for a class T. RefCounted * manages, well, refcounting for T, and because RefCounted is @@ -90,6 +92,8 @@ enum RefCountAtomicity template class RefCounted { + friend class RefPtr; + protected: RefCounted() : mRefCnt(0) {} ~RefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); } diff --git a/mfbt/RefPtr.h b/mfbt/RefPtr.h new file mode 100644 index 000000000000..a1ad86031ee4 --- /dev/null +++ b/mfbt/RefPtr.h @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Helpers for defining and using refcounted objects. */ + +#ifndef mozilla_RefPtr_h +#define mozilla_RefPtr_h + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" +#include "mozilla/Attributes.h" +#include "mozilla/Move.h" +#include "mozilla/RefCounted.h" +#include "mozilla/RefCountType.h" +#include "mozilla/nsRefPtr.h" +#include "mozilla/TypeTraits.h" +#if defined(MOZILLA_INTERNAL_API) +#include "nsXPCOM.h" +#endif + +#if defined(MOZILLA_INTERNAL_API) && \ + !defined(MOZILLA_XPCOMRT_API) && \ + (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) +#define MOZ_REFCOUNTED_LEAK_CHECKING +#endif + +namespace mozilla { + +template class OutParamRef; +template OutParamRef byRef(RefPtr&); + +/** + * RefPtr points to a refcounted thing that has AddRef and Release + * methods to increase/decrease the refcount, respectively. After a + * RefPtr is assigned a T*, the T* can be used through the RefPtr + * as if it were a T*. + * + * A RefPtr can forget its underlying T*, which results in the T* + * being wrapped in a temporary object until the T* is either + * re-adopted from or released by the temporary. + */ +template +class RefPtr +{ + // To allow them to use unref() + friend class OutParamRef; + + struct DontRef {}; + +public: + RefPtr() : mPtr(0) {} + RefPtr(const RefPtr& aOther) : mPtr(ref(aOther.mPtr)) {} + MOZ_IMPLICIT RefPtr(already_AddRefed& aOther) : mPtr(aOther.take()) {} + MOZ_IMPLICIT RefPtr(already_AddRefed&& aOther) : mPtr(aOther.take()) {} + MOZ_IMPLICIT RefPtr(T* aVal) : mPtr(ref(aVal)) {} + + template + MOZ_IMPLICIT RefPtr(const RefPtr& aOther) : mPtr(ref(aOther.get())) {} + + ~RefPtr() { unref(mPtr); } + + RefPtr& operator=(const RefPtr& aOther) + { + assign(ref(aOther.mPtr)); + return *this; + } + RefPtr& operator=(already_AddRefed& aOther) + { + assign(aOther.take()); + return *this; + } + RefPtr& operator=(already_AddRefed&& aOther) + { + assign(aOther.take()); + return *this; + } + RefPtr& operator=(T* aVal) + { + assign(ref(aVal)); + return *this; + } + + template + RefPtr& operator=(const RefPtr& aOther) + { + assign(ref(aOther.get())); + return *this; + } + + already_AddRefed forget() + { + T* tmp = mPtr; + mPtr = nullptr; + return already_AddRefed(tmp); + } + + T* get() const { return mPtr; } + operator T*() const +#ifdef MOZ_HAVE_REF_QUALIFIERS + & +#endif + { return mPtr; } + T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mPtr; } + T& operator*() const { return *mPtr; } + +#ifdef MOZ_HAVE_REF_QUALIFIERS + // Don't allow implicit conversion of temporary RefPtr to raw pointer, because + // the refcount might be one and the pointer will immediately become invalid. + operator T*() const && = delete; + + // Needed to avoid the deleted operator above + explicit operator bool() const { return !!mPtr; } +#endif + +private: + void assign(T* aVal) + { + T* tmp = mPtr; + mPtr = aVal; + unref(tmp); + } + + T* MOZ_OWNING_REF mPtr; + + static MOZ_ALWAYS_INLINE T* ref(T* aVal) + { + if (aVal) { + aVal->AddRef(); + } + return aVal; + } + + static MOZ_ALWAYS_INLINE void unref(T* aVal) + { + if (aVal) { + aVal->Release(); + } + } +}; + +/** + * OutParamRef is a wrapper that tracks a refcounted pointer passed as + * an outparam argument to a function. OutParamRef implements COM T** + * outparam semantics: this requires the callee to AddRef() the T* + * returned through the T** outparam on behalf of the caller. This + * means the caller (through OutParamRef) must Release() the old + * object contained in the tracked RefPtr. It's OK if the callee + * returns the same T* passed to it through the T** outparam, as long + * as the callee obeys the COM discipline. + * + * Prefer returning already_AddRefed from functions over creating T** + * outparams and passing OutParamRef to T**. Prefer RefPtr* + * outparams over T** outparams. + */ +template +class OutParamRef +{ + friend OutParamRef byRef(RefPtr&); + +public: + ~OutParamRef() + { + RefPtr::unref(mRefPtr.mPtr); + mRefPtr.mPtr = mTmp; + } + + operator T**() { return &mTmp; } + +private: + explicit OutParamRef(RefPtr& p) : mRefPtr(p), mTmp(p.get()) {} + + RefPtr& mRefPtr; + T* mTmp; + + OutParamRef() = delete; + OutParamRef& operator=(const OutParamRef&) = delete; +}; + +/** + * byRef cooperates with OutParamRef to implement COM outparam semantics. + */ +template +OutParamRef +byRef(RefPtr& aPtr) +{ + return OutParamRef(aPtr); +} + +} // namespace mozilla + +// Declared in nsRefPtr.h +template template +nsRefPtr::nsRefPtr(mozilla::RefPtr&& aOther) + : nsRefPtr(aOther.forget()) +{ +} + +template template +nsRefPtr& +nsRefPtr::operator=(mozilla::RefPtr&& aOther) +{ + assign_assuming_AddRef(aOther.forget().take()); + return *this; +} + +#endif /* mozilla_RefPtr_h */ diff --git a/mfbt/moz.build b/mfbt/moz.build index 63273d610136..c7d23819b521 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -70,6 +70,7 @@ EXPORTS.mozilla = [ 'RefCounted.h', 'RefCountType.h', 'ReentrancyGuard.h', + 'RefPtr.h', 'ReverseIterator.h', 'RollingMean.h', 'Scoped.h', diff --git a/mfbt/nsRefPtr.h b/mfbt/nsRefPtr.h index ee88666d9652..0ba5a69c2c9a 100644 --- a/mfbt/nsRefPtr.h +++ b/mfbt/nsRefPtr.h @@ -19,6 +19,7 @@ class nsCOMPtr_helper; namespace mozilla { template class OwningNonNull; +template class RefPtr; } // namespace mozilla template @@ -127,6 +128,10 @@ public: template MOZ_IMPLICIT nsRefPtr(const mozilla::OwningNonNull& aOther); + // Defined in RefPtr.h + template + MOZ_IMPLICIT nsRefPtr(mozilla::RefPtr&& aOther); + // Assignment operators nsRefPtr& @@ -187,6 +192,11 @@ public: nsRefPtr& operator=(const mozilla::OwningNonNull& aOther); + // Defined in RefPtr.h + template + nsRefPtr& + operator=(mozilla::RefPtr&& aOther); + // Other pointer operators void