Bug 1281090. Part 4 - prune disconnected listeners more aggressively to avoid hitting the assertion. r=gerald. draft
authorJW Wang <jwwang@mozilla.com>
Tue, 21 Jun 2016 16:31:03 +0800
changeset 380742 05c147239fa97d754439ce1154d34d29c22542df
parent 380741 d4ef0e44106f939ed0695e5eef6df181e9bd0ec9
child 380743 74b84944360afcd4ea50390153d071c8e63a74b4
push id21310
push userjwwang@mozilla.com
push dateThu, 23 Jun 2016 02:27:34 +0000
reviewersgerald
bugs1281090
milestone50.0a1
Bug 1281090. Part 4 - prune disconnected listeners more aggressively to avoid hitting the assertion. r=gerald. MozReview-Commit-ID: 1Z5L3swKBx6
dom/media/MediaEventSource.h
dom/media/gtest/TestMediaEventSource.cpp
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -402,20 +402,30 @@ class MediaEventSourceImpl {
 
   template<typename Target, typename Func>
   using ListenerImpl =
     detail::ListenerImpl<Dp, Target, Func, PassMode, ArgType<Es>...>;
 
   template <typename Method>
   using TakeArgs = detail::TakeArgs<Method>;
 
+  void PruneListeners() {
+    int32_t last = static_cast<int32_t>(mListeners.Length()) - 1;
+    for (int32_t i = last; i >= 0; --i) {
+      if (mListeners[i]->Token()->IsRevoked()) {
+        mListeners.RemoveElementAt(i);
+      }
+    }
+  }
+
   template<typename Target, typename Function>
   MediaEventListener
   ConnectInternal(Target* aTarget, const Function& aFunction) {
     MutexAutoLock lock(mMutex);
+    PruneListeners();
     MOZ_ASSERT(Lp == ListenerPolicy::NonExclusive || mListeners.IsEmpty());
     auto l = mListeners.AppendElement();
     l->reset(new ListenerImpl<Target, Function>(aTarget, aFunction));
     return MediaEventListener((*l)->Token());
   }
 
   // |Method| takes one or more arguments.
   template <typename Target, typename This, typename Method>
@@ -484,17 +494,18 @@ public:
   }
 
 protected:
   MediaEventSourceImpl() : mMutex("MediaEventSourceImpl::mMutex") {}
 
   template <typename... Ts>
   void NotifyInternal(Ts&&... aEvents) {
     MutexAutoLock lock(mMutex);
-    for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
+    int32_t last = static_cast<int32_t>(mListeners.Length()) - 1;
+    for (int32_t i = last; i >= 0; --i) {
       auto&& l = mListeners[i];
       // Remove disconnected listeners.
       // It is not optimal but is simple and works well.
       if (l->Token()->IsRevoked()) {
         mListeners.RemoveElementAt(i);
         continue;
       }
       l->Dispatch(Forward<Ts>(aEvents)...);
--- a/dom/media/gtest/TestMediaEventSource.cpp
+++ b/dom/media/gtest/TestMediaEventSource.cpp
@@ -118,16 +118,30 @@ TEST(MediaEventSource, DisconnectBeforeN
 
   EXPECT_EQ(i, 22); // 11 * 2
   EXPECT_EQ(j, 0); // event not received
 
   listener1.Disconnect();
 }
 
 /*
+ * Test we don't hit the assertion when calling Connect() and Disconnect()
+ * repeatedly.
+ */
+TEST(MediaEventSource, DisconnectAndConnect)
+{
+  RefPtr<TaskQueue> queue;
+  MediaEventProducerExc<int> source;
+  MediaEventListener listener = source.Connect(queue, [](){});
+  listener.Disconnect();
+  listener = source.Connect(queue, [](){});
+  listener.Disconnect();
+}
+
+/*
  * Test void event type.
  */
 TEST(MediaEventSource, VoidEventType)
 {
   RefPtr<TaskQueue> queue = new TaskQueue(
     GetMediaThreadPool(MediaThreadType::PLAYBACK));
 
   MediaEventProducer<void> source;