Bug 792954. Add a WeakPtr implementation to use instead of nsISupportsWeakReference. r=joe,ehsan,Waldo
This patch also replaces the usage of nsISupportsWeakReference in RasterImage as an example.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -199,21 +199,20 @@ namespace mozilla {
namespace image {
/* static */ StaticRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
/* static */ nsRefPtr<RasterImage::ScaleWorker> RasterImage::ScaleWorker::sSingleton;
/* static */ nsRefPtr<RasterImage::DrawWorker> RasterImage::DrawWorker::sSingleton;
static nsCOMPtr<nsIThread> 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
//******************************************************************************
RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
Image(aStatusTracker), // invoke superclass's constructor
mSize(0,0),
mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
mAnim(nullptr),
@@ -2552,17 +2551,17 @@ RasterImage::RequestDecode()
// If our callstack goes through a size decoder, we have a problem.
// We need to shutdown the size decode and replace it with a full
// decoder, but can't do that from within the decoder itself. Thus, we post
// an asynchronous event to the event loop to do it later. Since
// RequestDecode() is an asynchronous function this works fine (though it's
// a little slower).
if (mInDecoder) {
- nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(this);
+ nsRefPtr<imgDecodeRequestor> requestor = new imgDecodeRequestor(*this);
return NS_DispatchToCurrentThread(requestor);
}
// If we have a size decode open, interrupt it and shut it down; or if
// the decoder has different flags than what we need
if (mDecoder &&
(mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -28,16 +28,17 @@
#include "nsTArray.h"
#include "imgFrame.h"
#include "nsThreadUtils.h"
#include "DiscardTracker.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Telemetry.h"
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
+#include "mozilla/WeakPtr.h"
#ifdef DEBUG
#include "imgIContainerDebug.h"
#endif
class imgIDecoder;
class imgIContainerObserver;
class nsIInputStream;
@@ -131,17 +132,17 @@ class ImageContainer;
class Image;
}
namespace image {
class Decoder;
class RasterImage : public Image
, public nsIProperties
- , public nsSupportsWeakReference
+ , public SupportsWeakPtr<RasterImage>
#ifdef DEBUG
, public imgIContainerDebug
#endif
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROPERTIES
#ifdef DEBUG
@@ -806,26 +807,25 @@ protected:
//
// We use this class when someone calls requestDecode() from within a decode
// notification. Since requestDecode() involves modifying the decoder's state
// (for example, possibly shutting down a header-only decode and starting a
// full decode), we don't want to do this from inside a decoder.
class imgDecodeRequestor : public nsRunnable
{
public:
- imgDecodeRequestor(imgIContainer *aContainer) {
- mContainer = do_GetWeakReference(aContainer);
+ imgDecodeRequestor(RasterImage &aContainer) {
+ mContainer = aContainer.asWeakPtr();
}
NS_IMETHOD Run() {
- nsCOMPtr<imgIContainer> con = do_QueryReferent(mContainer);
- if (con)
- con->RequestDecode();
+ if (mContainer)
+ mContainer->RequestDecode();
return NS_OK;
}
private:
- nsWeakPtr mContainer;
+ WeakPtr<RasterImage> mContainer;
};
} // namespace image
} // namespace mozilla
#endif /* mozilla_imagelib_RasterImage_h_ */
new file mode 100644
--- /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<C>.
+ * class C : public SupportsWeakPtr<C>
+ * {
+ * 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<C> weak = ptr->asWeakPtr();
+ * WeakPtr<C> 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 <typename T> class WeakPtr;
+
+template <typename T>
+class SupportsWeakPtr
+{
+ public:
+ WeakPtr<T> asWeakPtr() {
+ if (!weakRef)
+ weakRef = new WeakReference(static_cast<T*>(this));
+ return WeakPtr<T>(weakRef);
+ }
+
+ protected:
+ ~SupportsWeakPtr() {
+ MOZ_STATIC_ASSERT((IsBaseOf<SupportsWeakPtr<T>, T>::value), "T must derive from SupportsWeakPtr<T>");
+ if (weakRef)
+ weakRef->detach();
+ }
+
+ private:
+ friend class WeakPtr<T>;
+
+ // This can live beyond the lifetime of the class derived from SupportsWeakPtr.
+ class WeakReference : public RefCounted<WeakReference>
+ {
+ public:
+ explicit WeakReference(T* ptr) : ptr(ptr) {}
+ T* get() const {
+ return ptr;
+ }
+
+ private:
+ friend class WeakPtr<T>;
+ friend class SupportsWeakPtr<T>;
+ void detach() {
+ ptr = nullptr;
+ }
+ T* ptr;
+ };
+
+ RefPtr<WeakReference> weakRef;
+};
+
+template <typename T>
+class WeakPtr
+{
+ public:
+ WeakPtr(const WeakPtr<T>& 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<T>;
+
+ explicit WeakPtr(const RefPtr<typename SupportsWeakPtr<T>::WeakReference> &o) : ref(o) {}
+
+ RefPtr<typename SupportsWeakPtr<T>::WeakReference> ref;
+};
+
+} // namespace mozilla
+
+#endif /* ifdef mozilla_WeakPtr_h_ */
--- a/mfbt/exported_headers.mk
+++ b/mfbt/exported_headers.mk
@@ -26,9 +26,10 @@ EXPORTS_mozilla += \
RefPtr.h \
Scoped.h \
StandardInteger.h \
SHA1.h \
ThreadLocal.h \
TypeTraits.h \
Types.h \
Util.h \
+ WeakPtr.h \
$(NULL)
--- a/mfbt/tests/Makefile.in
+++ b/mfbt/tests/Makefile.in
@@ -10,16 +10,17 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
STL_FLAGS =
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
# not available to MFBT, we have to reset these MOZ_GLUE*_LDFLAGS before including it
# and LIBS_ after including it. For WRAP_LDFLAGS, it shouldn't matter.
# See later comments in bug 732875.
MOZ_GLUE_PROGRAM_LDFLAGS=
new file mode 100644
--- /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<C>.
+class C : public SupportsWeakPtr<C>
+{
+ 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<C> weak = ptr->asWeakPtr();
+ WeakPtr<C> 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<A>
+{
+ 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<A> ptr = a->asWeakPtr();
+ {
+ WeakPtr<A> ptr2 = a->asWeakPtr();
+ MOZ_ASSERT(ptr->data == 5);
+ WeakPtr<A> ptr3 = a->asWeakPtr();
+ MOZ_ASSERT(ptr->data == 5);
+ }
+
+ delete a;
+ MOZ_ASSERT(!ptr);
+
+ delete a2;
+
+ example();
+
+ return 0;
+}
--- a/xpcom/glue/nsWeakReference.h
+++ b/xpcom/glue/nsWeakReference.h
@@ -4,16 +4,18 @@
* 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 nsWeakReference_h__
#define nsWeakReference_h__
// nsWeakReference.h
+// See mfbt/WeakPtr.h for a more typesafe C++ implementation of weak references
+
#include "nsIWeakReferenceUtils.h"
class nsWeakReference;
// Set IMETHOD_VISIBILITY to empty so that the class-level NS_COM declaration
// controls member method visibility.
#undef IMETHOD_VISIBILITY
#define IMETHOD_VISIBILITY NS_COM_GLUE