Bug 1425930: Handle Broadcast()->Notify() calling RemoveObserver() r=froyd
authorRandell Jesup <rjesup@jesup.org>
Mon, 21 May 2018 15:30:35 -0400
changeset 419253 a314710b0acd38afc7de74f0306f514b50d84463
parent 419252 4d511f7fc5b5c16fdfea91242dea6086cd57c8c3
child 419254 603804d62ce82bbb6fe906bcf928eb8e7b8ae9aa
push id34032
push usernbeleuzu@mozilla.com
push dateTue, 22 May 2018 09:50:22 +0000
treeherdermozilla-central@f85be0c4f056 [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,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