Bug 1580182 - Allow mozilla::SupportsWeakPtr be on base classes as well, r=froydnj
authorHonza Bambas <honzab.moz@firemni.cz>
Wed, 11 Sep 2019 19:44:17 +0000
changeset 493021 abcbc3635aefb5f3355ca2913bb5f0b0a5fc2441
parent 493020 058d5d7d5fee11b4f2e1e57355bfd58085a46c6d
child 493022 4dd5bc10fd86fd2fe29a08e3fd1a2716fb451aa6
push id95270
push userhonzab.moz@firemni.cz
push dateFri, 13 Sep 2019 08:29:55 +0000
treeherderautoland@abcbc3635aef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1580182
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1580182 - Allow mozilla::SupportsWeakPtr be on base classes as well, r=froydnj Differential Revision: https://phabricator.services.mozilla.com/D45515
mfbt/WeakPtr.h
mfbt/tests/TestWeakPtr.cpp
--- a/mfbt/WeakPtr.h
+++ b/mfbt/WeakPtr.h
@@ -159,17 +159,17 @@ struct WeakPtrTraits {
   }
 };
 
 namespace detail {
 
 // This can live beyond the lifetime of the class derived from
 // SupportsWeakPtr.
 template <class T>
-class WeakReference : public ::mozilla::RefCounted<WeakReference<T> > {
+class WeakReference : public ::mozilla::RefCounted<WeakReference<T>> {
  public:
   explicit WeakReference(T* p) : mPtr(p) {
     MOZ_WEAKPTR_INIT_THREAD_SAFETY_CHECK();
   }
 
   T* get() const {
     MOZ_WEAKPTR_ASSERT_THREAD_SAFETY();
     return mPtr;
@@ -234,46 +234,64 @@ class SupportsWeakPtr {
   friend class WeakPtr<const T>;
 
   WeakPtr<T> mSelfReferencingWeakPtr;
 };
 
 template <typename T>
 class WeakPtr {
   typedef detail::WeakReference<T> WeakReference;
+  typedef typename RemoveConst<T>::Type NonConstT;
 
  public:
   WeakPtr& operator=(const WeakPtr& aOther) {
     mRef = aOther.mRef;
     MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mRef);
     return *this;
   }
 
   WeakPtr(const WeakPtr& aOther) {
     // The thread safety check is performed inside of the operator= method.
     *this = aOther;
   }
 
-  WeakPtr& operator=(T* aOther) {
+  WeakPtr& operator=(decltype(nullptr)) {
+    if (!mRef || mRef->get()) {
+      // Ensure that mRef is dereferenceable in the uninitialized state.
+      mRef = new WeakReference(nullptr);
+    }
+    return *this;
+  }
+
+  WeakPtr& operator=(SupportsWeakPtr<NonConstT> const* aOther) {
     if (aOther) {
       *this = aOther->SelfReferencingWeakPtr();
     } else if (!mRef || mRef->get()) {
       // Ensure that mRef is dereferenceable in the uninitialized state.
       mRef = new WeakReference(nullptr);
     }
     // The thread safety check happens inside SelfReferencingWeakPtr
     // or is initialized in the WeakReference constructor.
     return *this;
   }
 
-  MOZ_IMPLICIT WeakPtr(T* aOther) {
-    *this = aOther;
-    MOZ_WEAKPTR_ASSERT_THREAD_SAFETY_DELEGATED(mRef);
+  WeakPtr& operator=(SupportsWeakPtr<NonConstT>* aOther) {
+    if (aOther) {
+      *this = aOther->SelfReferencingWeakPtr();
+    } else if (!mRef || mRef->get()) {
+      // Ensure that mRef is dereferenceable in the uninitialized state.
+      mRef = new WeakReference(nullptr);
+    }
+    // The thread safety check happens inside SelfReferencingWeakPtr
+    // or is initialized in the WeakReference constructor.
+    return *this;
   }
 
+  MOZ_IMPLICIT WeakPtr(T* aOther) { *this = aOther; }
+
   // Ensure that mRef is dereferenceable in the uninitialized state.
   WeakPtr() : mRef(new WeakReference(nullptr)) {}
 
   operator T*() const { return mRef->get(); }
   T& operator*() const { return *mRef->get(); }
 
   T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mRef->get(); }
 
--- a/mfbt/tests/TestWeakPtr.cpp
+++ b/mfbt/tests/TestWeakPtr.cpp
@@ -4,38 +4,63 @@
  * 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;
 
+static char IamB[] = "B";
+static char IamC[] = "C";
+static char IamD[] = "D";
+static char IamE[] = "E";
+
+class B : public SupportsWeakPtr<B> {
+ public:
+  char const* whoAmI() const { return IamB; }
+};
+
 // To have a class C support weak pointers, inherit from SupportsWeakPtr<C>.
 class C : public SupportsWeakPtr<C> {
  public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(C)
 
   int mNum;
 
   C() : mNum(0) {}
 
   ~C() {
     // Setting mNum in the destructor allows us to test against use-after-free
     // below
     mNum = 0xDEAD;
   }
 
+  char const* whoAmI() const { return IamC; }
+
   void act() {}
 
   bool isConst() { return false; }
 
   bool isConst() const { return true; }
 };
 
+// Supports weakptr for two base classes (B and C) and itself (D)
+class D : public B, public C, public SupportsWeakPtr<D> {
+ public:
+  char const* whoAmI() const { return IamD; }
+};
+
+// Derived from a class that supports weakptr, but doesn't implement itself
+// To check upcast works as expected
+class E : public D {
+ public:
+  char const* whoAmI() const { return IamE; }
+};
+
 bool isConst(C*) { return false; }
 
 bool isConst(const C*) { return true; }
 
 int main() {
   C* c1 = new C;
   MOZ_RELEASE_ASSERT(c1->mNum == 0);
 
@@ -105,9 +130,44 @@ int main() {
   MOZ_RELEASE_ASSERT(!w3const,
                      "Deleting an object should clear WeakPtr's to it.");
   MOZ_RELEASE_ASSERT(w2,
                      "Deleting an object should not clear WeakPtr that are not "
                      "pointing to it.");
 
   delete c2;
   MOZ_RELEASE_ASSERT(!w2, "Deleting an object should clear WeakPtr's to it.");
+
+  // Testing multiple base classes weak pointer support
+  D* d = new D;
+  WeakPtr<D> dd = d;
+  WeakPtr<const D> ddconst = d;
+  WeakPtr<C> dc = d;
+  WeakPtr<const C> dcconst = d;
+  WeakPtr<B> db = d;
+  WeakPtr<const B> dbconst = d;
+
+  MOZ_RELEASE_ASSERT(dd->whoAmI() == IamD);
+  MOZ_RELEASE_ASSERT(ddconst->whoAmI() == IamD);
+  MOZ_RELEASE_ASSERT(dc->whoAmI() == IamC);
+  MOZ_RELEASE_ASSERT(dcconst->whoAmI() == IamC);
+  MOZ_RELEASE_ASSERT(db->whoAmI() == IamB);
+  MOZ_RELEASE_ASSERT(dbconst->whoAmI() == IamB);
+
+  delete d;
+
+  MOZ_RELEASE_ASSERT(!dd);
+  MOZ_RELEASE_ASSERT(!ddconst);
+  MOZ_RELEASE_ASSERT(!dc);
+  MOZ_RELEASE_ASSERT(!dcconst);
+  MOZ_RELEASE_ASSERT(!db);
+  MOZ_RELEASE_ASSERT(!dbconst);
+
+  // Check that we correctly upcast to the base class supporting weakptr
+  E* e = new E;
+  WeakPtr<D> ed = e;
+
+  MOZ_RELEASE_ASSERT(ed->whoAmI() == IamD);
+
+  delete e;
+
+  MOZ_RELEASE_ASSERT(!ed);
 }