Bug 1445659 - Make it possible to store RefPtr<T> in AutoCleanLinkedList. r=froydnj
authorAndreas Farre <farre@mozilla.com>
Wed, 13 Jun 2018 06:25:00 +0300
changeset 486569 9c5f1bec200521f3d605604ff99beab5fbd8a1d7
parent 486568 0b294e23b3f65f9a8e62ae4789bfb0a0c9508222
child 486570 c70a61e3f34ace5748837cbf6d174587093c0fd9
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1445659
milestone63.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 1445659 - Make it possible to store RefPtr<T> in AutoCleanLinkedList. r=froydnj Add a trait method that AutoCleanLinkedList delegates to for calling delete on non-refcounted list elements.
mfbt/LinkedList.h
mfbt/tests/gtest/TestLinkedList.cpp
mfbt/tests/gtest/moz.build
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -94,28 +94,34 @@ struct LinkedListElementTraits
 
   // These static methods are called when an element is added to or removed from
   // a linked list. It can be used to keep track ownership in lists that are
   // supposed to own their elements. If elements are transferred from one list
   // to another, no enter or exit calls happen since the elements still belong
   // to a list.
   static void enterList(LinkedListElement<T>* elt) {}
   static void exitList(LinkedListElement<T>* elt) {}
+
+  // This method is called when AutoCleanLinkedList cleans itself
+  // during destruction. It can be used to call delete on elements if
+  // the list is the sole owner.
+  static void cleanElement(LinkedListElement<T>* elt) { delete elt->asT(); }
 };
 
 template<typename T>
 struct LinkedListElementTraits<RefPtr<T>>
 {
   typedef T* RawType;
   typedef const T* ConstRawType;
   typedef RefPtr<T> ClientType;
   typedef RefPtr<const T> ConstClientType;
 
   static void enterList(LinkedListElement<RefPtr<T>>* elt) { elt->asT()->AddRef(); }
   static void exitList(LinkedListElement<RefPtr<T>>* elt) { elt->asT()->Release(); }
+  static void cleanElement(LinkedListElement<RefPtr<T>>* elt) {}
 };
 
 } /* namespace detail */
 
 template<typename T>
 class LinkedList;
 
 template<typename T>
@@ -650,32 +656,35 @@ private:
 
   LinkedList& operator=(const LinkedList<T>& aOther) = delete;
   LinkedList(const LinkedList<T>& aOther) = delete;
 };
 
 template <typename T>
 class AutoCleanLinkedList : public LinkedList<T>
 {
+private:
+  using Traits = detail::LinkedListElementTraits<T>;
+  using ClientType = typename detail::LinkedListElementTraits<T>::ClientType;
 public:
   ~AutoCleanLinkedList()
   {
     clear();
   }
 
   AutoCleanLinkedList& operator=(AutoCleanLinkedList&& aOther)
   {
     LinkedList<T>::operator=(std::forward<LinkedList<T>>(aOther));
     return *this;
   }
 
   void clear()
   {
-    while (T* element = this->popFirst()) {
-      delete element;
+    while (ClientType element = this->popFirst()) {
+      Traits::cleanElement(element);
     }
   }
 };
 
 } /* namespace mozilla */
 
 #endif /* __cplusplus */
 
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/TestLinkedList.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "gtest/gtest.h"
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/RefPtr.h"
+
+using mozilla::AutoCleanLinkedList;
+using mozilla::LinkedList;
+using mozilla::LinkedListElement;
+
+class PtrClass : public LinkedListElement<PtrClass>
+{
+public:
+  bool* mResult;
+
+  explicit PtrClass(bool* result)
+    : mResult(result)
+  {
+    EXPECT_TRUE(!*mResult);
+  }
+
+  virtual ~PtrClass() {
+    *mResult = true;
+  }
+};
+
+class InheritedPtrClass : public PtrClass {
+public:
+  bool* mInheritedResult;
+
+  InheritedPtrClass(bool* result, bool* inheritedResult)
+    : PtrClass(result)
+    , mInheritedResult(inheritedResult)
+    {
+      EXPECT_TRUE(!*mInheritedResult);
+    }
+
+  virtual ~InheritedPtrClass() {
+    *mInheritedResult = true;
+  }
+};
+
+TEST(LinkedList, AutoCleanLinkedList)
+{
+    bool rv1 = false;
+    bool rv2 = false;
+    bool rv3 = false;
+    {
+        AutoCleanLinkedList<PtrClass> list;
+        list.insertBack(new PtrClass(&rv1));
+        list.insertBack(new InheritedPtrClass(&rv2, &rv3));
+    }
+
+    EXPECT_TRUE(rv1);
+    EXPECT_TRUE(rv2);
+    EXPECT_TRUE(rv3);
+}
+
+class CountedClass final : public LinkedListElement<RefPtr<CountedClass>>
+{
+public:
+  int mCount;
+  void AddRef() { mCount++; }
+  void Release() { mCount--; }
+
+  CountedClass()
+    : mCount(0)
+    {
+    }
+  ~CountedClass() { EXPECT_TRUE(mCount == 0); }
+};
+
+TEST(LinkedList, AutoCleanLinkedListRefPtr)
+{
+    RefPtr<CountedClass> elt1 = new CountedClass;
+    CountedClass* elt2 = new CountedClass;
+    {
+        AutoCleanLinkedList<RefPtr<CountedClass>> list;
+        list.insertBack(elt1);
+        list.insertBack(elt2);
+
+        EXPECT_TRUE(elt1->mCount == 2);
+        EXPECT_TRUE(elt2->mCount == 1);
+    }
+
+    EXPECT_TRUE(elt1->mCount == 1);
+    EXPECT_TRUE(elt2->mCount == 0);
+}
--- a/mfbt/tests/gtest/moz.build
+++ b/mfbt/tests/gtest/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 UNIFIED_SOURCES += [
+    'TestLinkedList.cpp',
     'TestSpan.cpp',
 ]
 
 #LOCAL_INCLUDES += [
 #    '../../base',
 #]
 
 FINAL_LIBRARY = 'xul-gtest'