diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index e8a7f5f13323..cc36ca287c61 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -204,11 +204,10 @@ namespace image { static nsCOMPtr sScaleWorkerThread = nullptr; #ifndef DEBUG -NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties, - nsISupportsWeakReference) +NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties) #else -NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties, - imgIContainerDebug, nsISupportsWeakReference) +NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties, + imgIContainerDebug) #endif //****************************************************************************** @@ -2557,7 +2556,7 @@ RasterImage::RequestDecode() // RequestDecode() is an asynchronous function this works fine (though it's // a little slower). if (mInDecoder) { - nsRefPtr requestor = new imgDecodeRequestor(this); + nsRefPtr requestor = new imgDecodeRequestor(*this); return NS_DispatchToCurrentThread(requestor); } diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 0d2c7dece6d9..5e5458c79c42 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -33,6 +33,7 @@ #include "mozilla/Telemetry.h" #include "mozilla/LinkedList.h" #include "mozilla/StaticPtr.h" +#include "mozilla/WeakPtr.h" #ifdef DEBUG #include "imgIContainerDebug.h" #endif @@ -136,7 +137,7 @@ class Decoder; class RasterImage : public Image , public nsIProperties - , public nsSupportsWeakReference + , public SupportsWeakPtr #ifdef DEBUG , public imgIContainerDebug #endif @@ -811,18 +812,17 @@ protected: class imgDecodeRequestor : public nsRunnable { public: - imgDecodeRequestor(imgIContainer *aContainer) { - mContainer = do_GetWeakReference(aContainer); + imgDecodeRequestor(RasterImage &aContainer) { + mContainer = aContainer.asWeakPtr(); } NS_IMETHOD Run() { - nsCOMPtr con = do_QueryReferent(mContainer); - if (con) - con->RequestDecode(); + if (mContainer) + mContainer->RequestDecode(); return NS_OK; } private: - nsWeakPtr mContainer; + WeakPtr mContainer; }; } // namespace image diff --git a/mfbt/WeakPtr.h b/mfbt/WeakPtr.h new file mode 100644 index 000000000000..e20767141e09 --- /dev/null +++ b/mfbt/WeakPtr.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +/* Weak pointer functionality, implemented as a mixin for use with any class. */ + +/** + * SupportsWeakPtr lets you have a pointer to an object 'Foo' without affecting + * its lifetime. It works by creating a single shared reference counted object + * (WeakReference) that each WeakPtr will access 'Foo' through. This lets 'Foo' + * clear the pointer in the WeakReference without having to know about all of + * the WeakPtrs to it and allows the WeakReference to live beyond the lifetime + * of 'Foo'. + * + * The overhead of WeakPtr is that accesses to 'Foo' becomes an additional + * dereference, and an additional heap allocated pointer sized object shared + * between all of the WeakPtrs. + * + * Example of usage: + * + * // To have a class C support weak pointers, inherit from SupportsWeakPtr. + * class C : public SupportsWeakPtr + * { + * public: + * int num; + * void act(); + * }; + * + * C* ptr = new C(); + * + * // Get weak pointers to ptr. The first time asWeakPtr is called + * // a reference counted WeakReference object is created that + * // can live beyond the lifetime of 'ptr'. The WeakReference + * // object will be notified of 'ptr's destruction. + * WeakPtr weak = ptr->asWeakPtr(); + * WeakPtr other = ptr->asWeakPtr(); + * + * // Test a weak pointer for validity before using it. + * if (weak) { + * weak->num = 17; + * weak->act(); + * } + * + * // Destroying the underlying object clears weak pointers to it. + * delete ptr; + * + * MOZ_ASSERT(!weak, "Deleting |ptr| clears weak pointers to it."); + * MOZ_ASSERT(!other, "Deleting |ptr| clears all weak pointers to it."); + * + * WeakPtr is typesafe and may be used with any class. It is not required that + * the class be reference-counted or allocated in any particular way. + * + * The API was loosely inspired by Chromium's weak_ptr.h: + * http://src.chromium.org/svn/trunk/src/base/memory/weak_ptr.h + */ + +#ifndef mozilla_WeakPtr_h_ +#define mozilla_WeakPtr_h_ + +#include "mozilla/Assertions.h" +#include "mozilla/NullPtr.h" +#include "mozilla/RefPtr.h" +#include "mozilla/TypeTraits.h" + +namespace mozilla { + +template class WeakPtr; + +template +class SupportsWeakPtr +{ + public: + WeakPtr asWeakPtr() { + if (!weakRef) + weakRef = new WeakReference(static_cast(this)); + return WeakPtr(weakRef); + } + + protected: + ~SupportsWeakPtr() { + MOZ_STATIC_ASSERT((IsBaseOf, T>::value), "T must derive from SupportsWeakPtr"); + if (weakRef) + weakRef->detach(); + } + + private: + friend class WeakPtr; + + // This can live beyond the lifetime of the class derived from SupportsWeakPtr. + class WeakReference : public RefCounted + { + public: + explicit WeakReference(T* ptr) : ptr(ptr) {} + T* get() const { + return ptr; + } + + private: + friend class WeakPtr; + friend class SupportsWeakPtr; + void detach() { + ptr = nullptr; + } + T* ptr; + }; + + RefPtr weakRef; +}; + +template +class WeakPtr +{ + public: + WeakPtr(const WeakPtr& o) : ref(o.ref) {} + WeakPtr() : ref(nullptr) {} + + operator T*() const { + return ref->get(); + } + T& operator*() const { + return *ref->get(); + } + + T* operator->() const { + return ref->get(); + } + + private: + friend class SupportsWeakPtr; + + explicit WeakPtr(const RefPtr::WeakReference> &o) : ref(o) {} + + RefPtr::WeakReference> ref; +}; + +} // namespace mozilla + +#endif /* ifdef mozilla_WeakPtr_h_ */ diff --git a/mfbt/exported_headers.mk b/mfbt/exported_headers.mk index 155704d9c468..adf07cf8a94f 100644 --- a/mfbt/exported_headers.mk +++ b/mfbt/exported_headers.mk @@ -31,4 +31,5 @@ EXPORTS_mozilla += \ TypeTraits.h \ Types.h \ Util.h \ + WeakPtr.h \ $(NULL) diff --git a/mfbt/tests/Makefile.in b/mfbt/tests/Makefile.in index 47a595d02404..542cffaf8cb0 100644 --- a/mfbt/tests/Makefile.in +++ b/mfbt/tests/Makefile.in @@ -15,6 +15,7 @@ CPP_UNIT_TESTS = \ TestCheckedInt.cpp \ TestTypeTraits.cpp \ TestSHA1.cpp \ + TestWeakPtr.cpp \ $(NULL) # in order to prevent rules.mk from trying to link to libraries that are diff --git a/mfbt/tests/TestWeakPtr.cpp b/mfbt/tests/TestWeakPtr.cpp new file mode 100644 index 000000000000..4b811a44ceea --- /dev/null +++ b/mfbt/tests/TestWeakPtr.cpp @@ -0,0 +1,76 @@ +/* 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/. */ + +#include "mozilla/WeakPtr.h" + +using mozilla::SupportsWeakPtr; +using mozilla::WeakPtr; + +// To have a class C support weak pointers, inherit from SupportsWeakPtr. +class C : public SupportsWeakPtr +{ + public: + int num; + void act() {} +}; + +static void +example() +{ + + C* ptr = new C(); + + // Get weak pointers to ptr. The first time asWeakPtr is called + // a reference counted WeakReference object is created that + // can live beyond the lifetime of 'ptr'. The WeakReference + // object will be notified of 'ptr's destruction. + WeakPtr weak = ptr->asWeakPtr(); + WeakPtr other = ptr->asWeakPtr(); + + // Test a weak pointer for validity before using it. + if (weak) { + weak->num = 17; + weak->act(); + } + + // Destroying the underlying object clears weak pointers to it. + delete ptr; + + MOZ_ASSERT(!weak, "Deleting |ptr| clears weak pointers to it."); + MOZ_ASSERT(!other, "Deleting |ptr| clears all weak pointers to it."); +} + +struct A : public SupportsWeakPtr +{ + int data; +}; + + +int main() +{ + + A* a = new A; + + // a2 is unused to test the case when we haven't initialized + // the internal WeakReference pointer. + A* a2 = new A; + + a->data = 5; + WeakPtr ptr = a->asWeakPtr(); + { + WeakPtr ptr2 = a->asWeakPtr(); + MOZ_ASSERT(ptr->data == 5); + WeakPtr ptr3 = a->asWeakPtr(); + MOZ_ASSERT(ptr->data == 5); + } + + delete a; + MOZ_ASSERT(!ptr); + + delete a2; + + example(); + + return 0; +} diff --git a/xpcom/glue/nsWeakReference.h b/xpcom/glue/nsWeakReference.h index 8bdf63a96527..79ebc47c23e4 100644 --- a/xpcom/glue/nsWeakReference.h +++ b/xpcom/glue/nsWeakReference.h @@ -9,6 +9,8 @@ // nsWeakReference.h +// See mfbt/WeakPtr.h for a more typesafe C++ implementation of weak references + #include "nsIWeakReferenceUtils.h" class nsWeakReference;