Bug 1425930 - Handle Broadcast()->Notify() calling RemoveObserver(). r=froydnj, a=abillings
authorRandell Jesup <rjesup@jesup.org>
Mon, 21 May 2018 15:30:35 -0400
changeset 802259 633bd6d37172b383fc47c086aaf6df6d951c9dd5
parent 802258 767ea821427e22b343932bd91af1f4cbb57a5d64
child 802260 a5ab68f0fdf09446f9af2c87854018edea00b916
push id111850
push userbmo:tom@mozilla.com
push dateThu, 31 May 2018 16:41:37 +0000
reviewersfroydnj, abillings
bugs1425930
milestone60.0.2
Bug 1425930 - Handle Broadcast()->Notify() calling RemoveObserver(). r=froydnj, a=abillings
xpcom/ds/Observer.h
--- a/xpcom/ds/Observer.h
+++ b/xpcom/ds/Observer.h
@@ -52,32 +52,52 @@ public:
   }
 
   /**
    * Remove the observer from the observer list.
    * @return Whether the observer has been found in the list.
    */
   bool RemoveObserver(Observer<T>* aObserver)
   {
-    return mObservers.RemoveElement(aObserver);
+    if (mObservers.RemoveElement(aObserver)) {
+      if (!mBroadcastCopy.IsEmpty()) {
+        // Annoyingly, someone could RemoveObserver() an item on the list
+        // while we're in a Broadcast()'s Notify() call.
+        auto i = mBroadcastCopy.IndexOf(aObserver);
+        MOZ_ASSERT(i != mBroadcastCopy.NoIndex);
+        mBroadcastCopy[i] = nullptr;
+      }
+      return true;
+    }
+    return false;
   }
 
   uint32_t Length()
   {
     return mObservers.Length();
   }
 
+  /**
+   * Call Notify() on each item in the list.
+   * Handles the case of Notify() calling RemoveObserver()
+   */
   void Broadcast(const T& aParam)
   {
-    nsTArray<Observer<T>*> observersCopy(mObservers);
-    uint32_t size = observersCopy.Length();
+    MOZ_ASSERT(mBroadcastCopy.IsEmpty());
+    mBroadcastCopy = mObservers;
+    uint32_t size = mBroadcastCopy.Length();
     for (uint32_t i = 0; i < size; ++i) {
-      observersCopy[i]->Notify(aParam);
+      // nulled if Removed during Broadcast
+      if (mBroadcastCopy[i]) {
+        mBroadcastCopy[i]->Notify(aParam);
+      }
     }
+    mBroadcastCopy.Clear();
   }
 
 protected:
   nsTArray<Observer<T>*> mObservers;
+  nsTArray<Observer<T>*> mBroadcastCopy;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_Observer_h