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 473475 9f007f15b9cf5d98308f0a9f1e1356733dc78c11
parent 473474 84df66c7e5457a1e93b9b92a9f1987494c0148bb
child 473476 2f803318f71f2aa6c3f4ef2edc00305f52f86e38
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, abillings
bugs1425930
milestone61.0
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