Bug 1281090. Part 4 - prune disconnected listeners more aggressively to avoid hitting the assertion. r=gerald.
MozReview-Commit-ID: 1Z5L3swKBx6
--- 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;