Bug 1027172 - Part 1: AudioChannelService should send notifications at the same way in IPC or not. r=mchen, a=sledru
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 24 Jun 2014 22:15:12 -0700
changeset 207711 46c8bd676a5057c23cdd0b2275efff3b7c86a079
parent 207710 692e8a051e75ed78bb43dabd09dcd964c3251b9c
child 207712 3ee7e6b1dc88492d8aa4a31053ddf6b2b52e40a3
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmchen, sledru
bugs1027172
milestone32.0a2
Bug 1027172 - Part 1: AudioChannelService should send notifications at the same way in IPC or not. r=mchen, a=sledru
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/audiochannel/tests/TestAudioChannelService.cpp
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -178,17 +178,17 @@ AudioChannelService::RegisterType(AudioC
     else if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
         mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
       mPlayableHiddenContentChildID = aChildID;
     }
 
     // In order to avoid race conditions, it's safer to notify any existing
     // agent any time a new one is registered.
     SendAudioChannelChangedNotification(aChildID);
-    Notify();
+    SendNotification();
   }
 }
 
 void
 AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
 {
   if (mDisabled) {
     return;
@@ -272,17 +272,17 @@ AudioChannelService::UnregisterTypeInter
     }
 
     if (aWithVideo) {
       MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID));
       mWithVideoChildIDs.RemoveElement(aChildID);
     }
 
     SendAudioChannelChangedNotification(aChildID);
-    Notify();
+    SendNotification();
   }
 }
 
 void
 AudioChannelService::UpdateChannelType(AudioChannel aChannel,
                                        uint64_t aChildID,
                                        bool aElementHidden,
                                        bool aElementWasHidden)
@@ -343,17 +343,17 @@ AudioChannelService::GetStateInternal(Au
   AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden);
   AudioChannelInternalType oldType = GetInternalType(aChannel,
                                                      aElementWasHidden);
 
   if (newType != oldType &&
       (aChannel == AudioChannel::Content ||
        (aChannel == AudioChannel::Normal &&
         mWithVideoChildIDs.Contains(aChildID)))) {
-    Notify();
+    SendNotification();
   }
 
   SendAudioChannelChangedNotification(aChildID);
 
   // Let play any visible audio channel.
   if (!aElementHidden) {
     if (CheckVolumeFadedCondition(newType, aElementHidden)) {
       return AUDIO_CHANNEL_STATE_FADED;
@@ -596,20 +596,51 @@ PLDHashOperator
 AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent,
                                       AudioChannelAgentData* aData, void* aUnused)
 {
   MOZ_ASSERT(aAgent);
   aAgent->NotifyAudioChannelStateChanged();
   return PL_DHASH_NEXT;
 }
 
+class NotifyRunnable : public nsRunnable
+{
+public:
+  NotifyRunnable(AudioChannelService* aService)
+    : mService(aService)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    mService->Notify();
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<AudioChannelService> mService;
+};
+
+void
+AudioChannelService::SendNotification()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mRunnable) {
+    return;
+  }
+
+  mRunnable = new NotifyRunnable(this);
+  NS_DispatchToCurrentThread(mRunnable);
+}
+
 void
 AudioChannelService::Notify()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  mRunnable = nullptr;
 
   // Notify any agent for the main process.
   mAgents.EnumerateRead(NotifyEnumerator, nullptr);
 
   // Notify for the child processes.
   nsTArray<ContentParent*> children;
   ContentParent::GetAll(children);
   for (uint32_t i = 0; i < children.Length(); i++) {
@@ -694,17 +725,17 @@ AudioChannelService::Observe(nsISupports
       while ((index = mWithVideoChildIDs.IndexOf(childID)) != -1) {
         mWithVideoChildIDs.RemoveElementAt(index);
       }
 
       // We don't have to remove the agents from the mAgents hashtable because if
       // that table contains only agents running on the same process.
 
       SendAudioChannelChangedNotification(childID);
-      Notify();
+      SendNotification();
 
       if (mDefChannelChildID == childID) {
         SetDefaultVolumeControlChannelInternal(-1, false, childID);
         mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
       }
     } else {
       NS_WARNING("ipc:content-shutdown message without childID property");
     }
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -13,16 +13,17 @@
 #include "nsITimer.h"
 
 #include "AudioChannelCommon.h"
 #include "AudioChannelAgent.h"
 #include "nsAttrValue.h"
 #include "nsClassHashtable.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 
+class nsIRunnable;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
 class AudioChannelService
@@ -114,18 +115,20 @@ public:
 #endif
 
   static const nsAttrValue::EnumTable* GetAudioChannelTable();
   static AudioChannel GetAudioChannel(const nsAString& aString);
   static AudioChannel GetDefaultAudioChannel();
   static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString);
   static void GetDefaultAudioChannelString(nsAString& aString);
 
+  void Notify();
+
 protected:
-  void Notify();
+  void SendNotification();
 
   /**
    * Send the audio-channel-changed notification for the given process ID if
    * needed.
    */
   void SendAudioChannelChangedNotification(uint64_t aChildID);
 
   /* Register/Unregister IPC types: */
@@ -238,16 +241,18 @@ protected:
   //   2. When there is a process with registered content channel(s) goes from
   //   background into foreground.
   //   3. When this process unregisters all hidden content channels.
   //   4. When this process shuts down.
   uint64_t mPlayableHiddenContentChildID;
 
   bool mDisabled;
 
+  nsCOMPtr<nsIRunnable> mRunnable;
+
   nsCOMPtr<nsITimer> mDeferTelChannelTimer;
   bool mTimerElementHidden;
   uint64_t mTimerChildID;
 
   uint64_t mDefChannelChildID;
 
   // This is needed for IPC comunication between
   // AudioChannelServiceChild and this class.
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -8,28 +8,41 @@
 #endif
 
 #include "TestHarness.h"
 
 #include "nsWeakReference.h"
 #include "AudioChannelService.h"
 #include "AudioChannelAgent.h"
 
+#include "nsThreadUtils.h"
+
 #define TEST_ENSURE_BASE(_test, _msg)       \
   PR_BEGIN_MACRO                            \
     if (!(_test)) {                         \
       fail(_msg);                           \
       return NS_ERROR_FAILURE;              \
     } else {                                \
       passed(_msg);                         \
     }                                       \
   PR_END_MACRO
 
 using namespace mozilla::dom;
 
+void
+spin_events_loop_until_false(const bool* const aCondition)
+{
+  nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
+  nsresult rv = NS_OK;
+  bool processed = true;
+  while (*aCondition && NS_SUCCEEDED(rv)) {
+    rv = thread->ProcessNextEvent(true, &processed);
+  }
+}
+
 class Agent : public nsIAudioChannelAgentCallback,
               public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
 
   Agent(AudioChannel aChannel)
   : mChannel(aChannel)
@@ -72,27 +85,17 @@ public:
     nsresult rv = mAgent->StartPlaying((int32_t *)_ret);
     mRegistered = true;
     return rv;
   }
 
   nsresult StopPlaying()
   {
     mRegistered = false;
-    int loop = 0;
-    while (mWaitCallback) {
-      #ifdef XP_WIN
-      Sleep(1000);
-      #else
-      sleep(1);
-      #endif
-      if (loop++ == 5) {
-        TEST_ENSURE_BASE(false, "StopPlaying timeout");
-      }
-    }
+    spin_events_loop_until_false(&mWaitCallback);
     return mAgent->StopPlaying();
   }
 
   nsresult SetVisibilityState(bool visible)
   {
     if (mRegistered) {
       mWaitCallback = true;
     }
@@ -106,29 +109,23 @@ public:
     return NS_OK;
   }
 
   NS_IMETHODIMP WindowVolumeChanged()
   {
     return NS_OK;
   }
 
-  nsresult GetCanPlay(AudioChannelState *_ret)
+  nsresult GetCanPlay(AudioChannelState *_ret, bool aWaitCallback = false)
   {
-    int loop = 0;
-    while (mWaitCallback) {
-      #ifdef XP_WIN
-      Sleep(1000);
-      #else
-      sleep(1);
-      #endif
-      if (loop++ == 5) {
-        TEST_ENSURE_BASE(false, "GetCanPlay timeout");
-      }
+    if (aWaitCallback) {
+      mWaitCallback = true;
     }
+
+    spin_events_loop_until_false(&mWaitCallback);
     *_ret = mCanPlay;
     return NS_OK;
   }
 
   nsCOMPtr<nsIAudioChannelAgent> mAgent;
   AudioChannel mChannel;
   bool mWaitCallback;
   bool mRegistered;
@@ -342,17 +339,17 @@ TestFadedState()
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test4: A content channel visible agent must be playable");
 
   rv = notificationAgent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test4: A notification channel visible agent must be playable");
 
-    rv = contentAgent->GetCanPlay(&playable);
+  rv = contentAgent->GetCanPlay(&playable, true);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED,
     "Test4: A content channel unvisible agent must be faded because of "
     "notification channel is playing");
 
   rv = contentAgent->SetVisibilityState(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -369,17 +366,17 @@ TestFadedState()
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test4: A notification channel unvisible agent must be playable from "
     "foreground to background");
 
   rv = notificationAgent->StopPlaying();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = contentAgent->GetCanPlay(&playable);
+  rv = contentAgent->GetCanPlay(&playable, true);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test4: A content channel unvisible agent must be playable "
     "because of notification channel is stopped");
 
   rv = contentAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);