Bug 1425930: Handle Broadcast()->Notify() calling RemoveObserver() r=froyd
☠☠ backed out by 1f0b7e9a6450 ☠ ☠
authorRandell Jesup <rjesup@jesup.org>
Mon, 21 May 2018 10:35:08 -0400
changeset 419166 9d111a95cfbbe2dea1427d22ab342a584d9c8af6
parent 419165 63b24cc63ecaa5cde91b448d1b5066a1b1bad3aa
child 419167 f7b8d0d06d29026e5604d3bf8725820a681f36e0
push id34029
push usershindli@mozilla.com
push dateMon, 21 May 2018 21:30:22 +0000
treeherdermozilla-central@51f2535c7974 [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 != 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