diff options
Diffstat (limited to 'gfx/layers/AtomicRefCountedWithFinalize.h')
-rw-r--r-- | gfx/layers/AtomicRefCountedWithFinalize.h | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/gfx/layers/AtomicRefCountedWithFinalize.h b/gfx/layers/AtomicRefCountedWithFinalize.h new file mode 100644 index 000000000..7bd8f0203 --- /dev/null +++ b/gfx/layers/AtomicRefCountedWithFinalize.h @@ -0,0 +1,196 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +#ifndef MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_ +#define MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_ + +#include "mozilla/RefPtr.h" +#include "mozilla/Likely.h" +#include "MainThreadUtils.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "mozilla/gfx/Logging.h" + +#define ADDREF_MANUALLY(obj) (obj)->AddRefManually(__FUNCTION__, __FILE__, __LINE__) +#define RELEASE_MANUALLY(obj) (obj)->ReleaseManually(__FUNCTION__, __FILE__, __LINE__) + +namespace mozilla { + +template<class U> +class StaticRefPtr; + +namespace gl { +template<typename T> +class RefSet; + +template<typename T> +class RefQueue; +} // namespace gl + +template<typename T> +class AtomicRefCountedWithFinalize +{ +protected: + explicit AtomicRefCountedWithFinalize(const char* aName) + : mRecycleCallback(nullptr) + , mRefCount(0) +#ifdef DEBUG + , mSpew(false) + , mManualAddRefs(0) + , mManualReleases(0) +#endif +#ifdef NS_BUILD_REFCNT_LOGGING + , mName(aName) +#endif + {} + + ~AtomicRefCountedWithFinalize() { + if (mRefCount >= 0) { + gfxCriticalError() << "Deleting referenced object? " << mRefCount; + } + } + +public: + // Mark user classes that are considered flawless. + template<class U> + friend class ::mozilla::StaticRefPtr; + + template<class U> + friend struct mozilla::RefPtrTraits; + + template<typename U> + friend class ::mozilla::gl::RefSet; + + template<typename U> + friend class ::mozilla::gl::RefQueue; + + //friend class mozilla::gl::SurfaceFactory; + + void AddRefManually(const char* funcName, const char* fileName, uint32_t lineNum) { +#ifdef DEBUG + uint32_t count = ++mManualAddRefs; + if (mSpew) { + printf_stderr("AddRefManually() #%u in %s at %s:%u\n", count, funcName, + fileName, lineNum); + } +#else + (void)funcName; + (void)fileName; + (void)lineNum; +#endif + AddRef(); + } + + void ReleaseManually(const char* funcName, const char* fileName, uint32_t lineNum) { +#ifdef DEBUG + uint32_t count = ++mManualReleases; + if (mSpew) { + printf_stderr("ReleaseManually() #%u in %s at %s:%u\n", count, funcName, + fileName, lineNum); + } +#else + (void)funcName; + (void)fileName; + (void)lineNum; +#endif + Release(); + } + +private: + void AddRef() { + MOZ_ASSERT(mRefCount >= 0, "AddRef() during/after Finalize()/dtor."); + mRefCount++; + NS_LOG_ADDREF(this, mRefCount, mName, sizeof(*this)); + } + + void Release() { + MOZ_ASSERT(mRefCount > 0, "Release() during/after Finalize()/dtor."); + // Read mRecycleCallback early so that it does not get set to + // deleted memory, if the object is goes away. See bug 994903. + // This saves us in the case where there is no callback, so that + // we can do the "else if" below. + RecycleCallback recycleCallback = mRecycleCallback; + int currCount = --mRefCount; + if (currCount < 0) { + gfxCriticalError() << "Invalid reference count release" << currCount; + ++mRefCount; + return; + } + NS_LOG_RELEASE(this, currCount, mName); + + if (0 == currCount) { + mRefCount = detail::DEAD; + MOZ_ASSERT(IsDead()); + + // Recycle listeners must call ClearRecycleCallback + // before releasing their strong reference. + if (mRecycleCallback) { + gfxCriticalError() << "About to release with valid callback"; + mRecycleCallback = nullptr; + } + + MOZ_ASSERT(mManualAddRefs == mManualReleases); + + T* derived = static_cast<T*>(this); + derived->Finalize(); + delete derived; + } else if (1 == currCount && recycleCallback) { + // There is nothing enforcing this in the code, except how the callers + // are being careful to never let the reference count go down if there + // is a callback. + MOZ_ASSERT(!IsDead()); + T* derived = static_cast<T*>(this); + recycleCallback(derived, mClosure); + } + } + +public: + typedef void (*RecycleCallback)(T* aObject, void* aClosure); + /** + * Set a callback responsible for recycling this object + * before it is finalized. + */ + void SetRecycleCallback(RecycleCallback aCallback, void* aClosure) + { + MOZ_ASSERT(!IsDead()); + mRecycleCallback = aCallback; + mClosure = aClosure; + } + void ClearRecycleCallback() + { + MOZ_ASSERT(!IsDead()); + SetRecycleCallback(nullptr, nullptr); + } + + bool HasRecycleCallback() const + { + MOZ_ASSERT(!IsDead()); + return !!mRecycleCallback; + } + + bool IsDead() const + { + return mRefCount < 0; + } + +private: + RecycleCallback mRecycleCallback; + void *mClosure; + Atomic<int> mRefCount; +#ifdef DEBUG +public: + bool mSpew; +private: + Atomic<uint32_t> mManualAddRefs; + Atomic<uint32_t> mManualReleases; +#endif +#ifdef NS_BUILD_REFCNT_LOGGING + const char* mName; +#endif +}; + +} // namespace mozilla + +#endif |