Bug 803688 - Remove LinkedListElements from their list when they're destructed, and assert that a LinkedList is empty when it's destructed. r=waldo
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -8,16 +8,20 @@
/*
* The classes LinkedList<T> and LinkedListElement<T> together form a
* convenient, type-safe doubly-linked list implementation.
*
* The class T which will be inserted into the linked list must inherit from
* LinkedListElement<T>. A given object may be in only one linked list at a
* time.
*
+ * A LinkedListElement automatically removes itself from the list upon
+ * destruction, and a LinkedList will fatally assert in debug builds if it's
+ * non-empty when it's destructed.
+ *
* For example, you might use LinkedList in a simple observer list class as
* follows.
*
* class Observer : public LinkedListElement<Observer>
* {
* public:
* void observe(char* topic) { ... }
* };
@@ -105,16 +109,21 @@ class LinkedListElement
public:
LinkedListElement()
: next(thisDuringConstruction()),
prev(thisDuringConstruction()),
isSentinel(false)
{ }
+ ~LinkedListElement() {
+ if (!isSentinel && isInList())
+ remove();
+ }
+
/*
* Get the next element in the list, or NULL if this is the last element in
* the list.
*/
T* getNext() {
return next->asT();
}
const T* getNext() const {
@@ -179,18 +188,17 @@ class LinkedListElement
NODE_KIND_NORMAL,
NODE_KIND_SENTINEL
};
LinkedListElement(NodeKind nodeKind)
: next(this),
prev(this),
isSentinel(nodeKind == NODE_KIND_SENTINEL)
- {
- }
+ { }
/*
* Return |this| cast to T* if we're a normal node, or return NULL if we're
* a sentinel node.
*/
T* asT() {
if (isSentinel)
return NULL;
@@ -241,16 +249,20 @@ template<typename T>
class LinkedList
{
private:
LinkedListElement<T> sentinel;
public:
LinkedList() : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL) { }
+ ~LinkedList() {
+ MOZ_ASSERT(isEmpty());
+ }
+
/*
* Add elem to the front of the list.
*/
void insertFront(T* elem) {
/* Bypass setNext()'s this->isInList() assertion. */
sentinel.setNextUnsafe(elem);
}