Bug 792954. Add a WeakPtr implementation to use instead of nsISupportsWeakReference. r=joe,ehsan,Waldo
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 04 Oct 2012 15:45:07 -0400
changeset 115575 a2630fb2dbfa056a44106b59c84bfc7f9351a943
parent 115574 0fdefdb3481df9214851338cfc6c47ca9fa635d9
child 115576 68dd2bcc51ee55e1ce3dd5e77d655e6e0cc60756
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersjoe, ehsan, Waldo
bugs792954
milestone18.0a1
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.
image/src/RasterImage.cpp
image/src/RasterImage.h
mfbt/WeakPtr.h
mfbt/exported_headers.mk
mfbt/tests/Makefile.in
mfbt/tests/TestWeakPtr.cpp
xpcom/glue/nsWeakReference.h
--- 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