Bug 1425930 - Handle Broadcast()->Notify() calling RemoveObserver(). r=froyd
☠☠ backed out by b19af5ec19d9 ☠ ☠
authorRandell Jesup <rjesup@jesup.org>
Fri, 18 May 2018 11:15:30 -0400
changeset 419115 4dce0b1b22af0eaaa01030de35c97cd3af473305
parent 419114 76a4f60761f972e768327e3d65bafa2fe0c8b1cb
child 419116 724b88e1831d618572ae70d9c6a8ae67171a5669
push id34026
push userapavel@mozilla.com
push dateMon, 21 May 2018 09:47:33 +0000
treeherdermozilla-central@dc1868d255be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroyd
bugs1425930
milestone62.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 1425930 - Handle Broadcast()->Notify() calling RemoveObserver(). r=froyd
xpcom/ds/Observer.h
--- a/xpcom/ds/Observer.h
+++ b/xpcom/ds/Observer.h
@@ -52,32 +52,50 @@ 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)) {
+      // 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 != 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.Empty());
+    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