diff --git a/mfbt/LinkedList.h b/mfbt/LinkedList.h index c299ec6fcaef..cc394f685a07 100644 --- a/mfbt/LinkedList.h +++ b/mfbt/LinkedList.h @@ -99,6 +99,11 @@ struct LinkedListElementTraits // to a list. static void enterList(LinkedListElement* elt) {} static void exitList(LinkedListElement* 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* elt) { delete elt->asT(); } }; template @@ -111,6 +116,7 @@ struct LinkedListElementTraits> static void enterList(LinkedListElement>* elt) { elt->asT()->AddRef(); } static void exitList(LinkedListElement>* elt) { elt->asT()->Release(); } + static void cleanElement(LinkedListElement>* elt) {} }; } /* namespace detail */ @@ -655,6 +661,9 @@ private: template class AutoCleanLinkedList : public LinkedList { +private: + using Traits = detail::LinkedListElementTraits; + using ClientType = typename detail::LinkedListElementTraits::ClientType; public: ~AutoCleanLinkedList() { @@ -669,8 +678,8 @@ public: void clear() { - while (T* element = this->popFirst()) { - delete element; + while (ClientType element = this->popFirst()) { + Traits::cleanElement(element); } } }; diff --git a/mfbt/tests/gtest/TestLinkedList.cpp b/mfbt/tests/gtest/TestLinkedList.cpp new file mode 100644 index 000000000000..f6be7baf6235 --- /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 +{ +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 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> +{ +public: + int mCount; + void AddRef() { mCount++; } + void Release() { mCount--; } + + CountedClass() + : mCount(0) + { + } + ~CountedClass() { EXPECT_TRUE(mCount == 0); } +}; + +TEST(LinkedList, AutoCleanLinkedListRefPtr) +{ + RefPtr elt1 = new CountedClass; + CountedClass* elt2 = new CountedClass; + { + AutoCleanLinkedList> 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); +} diff --git a/mfbt/tests/gtest/moz.build b/mfbt/tests/gtest/moz.build index bd559d60b94e..85243ade98aa 100644 --- a/mfbt/tests/gtest/moz.build +++ b/mfbt/tests/gtest/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. UNIFIED_SOURCES += [ + 'TestLinkedList.cpp', 'TestSpan.cpp', ]