Bug 842679 - Part 3: Wait for a tab-child to be created before modifying priorities in the ProcessPriorityManager. r=cjones
authorJustin Lebar <justin.lebar@gmail.com>
Fri, 22 Feb 2013 23:24:28 -0500
changeset 122718 67bee77ac7e37ab95dfd47c35783ccef47af570c
parent 122717 93ccd4912321292701928ab69dd3885e1f088927
child 122719 1d8799803f37bb83cc8ce5f997206999b25ccb28
push id24356
push usergszorc@mozilla.com
push dateSun, 24 Feb 2013 01:00:12 +0000
treeherdermozilla-central@195e706140d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs842679
milestone22.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 842679 - Part 3: Wait for a tab-child to be created before modifying priorities in the ProcessPriorityManager. r=cjones
dom/ipc/ProcessPriorityManager.cpp
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -156,25 +156,31 @@ public:
    * ProcessPriorityManager.h::TemporarilyLockProcessPriority().
    */
   void TemporarilyLockProcessPriority();
 
   /**
    * Recompute this process's priority and apply it, potentially after a brief
    * delay.
    *
-   * If the new priority is FOREGROUND*, it takes effect immediately.
+   * If we are transitioning to a priority that is "lower" than the current
+   * priority (as defined below), that transition happens after a grace period.
+   * Otherwise the transition happens immediately.
+   *
+   * For the purposes of deciding whether to apply a grace period, the
+   * hierarchy of priorities is
    *
-   * If the new priority is a BACKGROUND* priority and this process's priority
-   * is currently a BACKGROUND* priority, the new priority takes effect
-   * immediately.
+   *  - UNKNOWN
+   *  - FOREGROUND_HIGH
+   *  - FOREGROUND
+   *  - BACKGROUND*
    *
-   * But if the new priority is a BACKGROUND* priority and this process is not
-   * currently in the background, we schedule a timer and run
-   * ResetPriorityNow() after a short period of time.
+   * So for example, a transition between any two BACKGROUND* priorites happens
+   * immediately, but a transition from UNKNOWN to FOREGROUND_HIGH happens
+   * after a grace period.
    */
   void ResetPriority();
 
   /**
    * Recompute this process's priority and apply it immediately.
    */
   void ResetPriorityNow();
 
@@ -197,21 +203,29 @@ private:
    */
   ProcessPriority GetBackgroundPriority();
 
   /**
    * Compute whether this process is in the foreground and return the result.
    */
   bool ComputeIsInForeground();
 
+
+  /**
+   * Set this process's priority to the appropriate FOREGROUND* priority
+   * immediately if we're upgrading its priority, and after a grace period if
+   * we're downgrading it or if the current priority is unknown.
+   */
+  void SetIsForeground();
+
   /**
    * Set this process's priority to the appropriate FOREGROUND* priority
    * immediately.
    */
-  void SetIsForeground();
+  void SetIsForegroundNow();
 
   /**
    * Set this process's priority to the appropriate BACKGROUND* priority
    * immediately.
    */
   void SetIsBackgroundNow();
 
   /**
@@ -226,31 +240,36 @@ private:
   bool mHoldsCPUWakeLock;
 
   // Tracks whether this process holds the "high-priority" lock.
   bool mHoldsHighPriorityWakeLock;
 
   // mProcessPriority tracks the priority we've given this process in hal.
   ProcessPriority mProcessPriority;
 
+  // Have we seen at least one tab-child-created event yet?  Until this is
+  // true, ResetPriority() and ResetPriorityNow() do nothing.
+  bool mObservedTabChildCreated;
+
   nsTArray<nsWeakPtr> mWindows;
 
   // When this timer expires, we set mResetPriorityTimer to null and run
   // ResetPriorityNow().
   nsCOMPtr<nsITimer> mResetPriorityTimer;
 
   nsWeakPtr mMemoryMinimizerRunnable;
 };
 
 NS_IMPL_ISUPPORTS2(ProcessPriorityManager, nsIObserver, nsIDOMEventListener)
 
 ProcessPriorityManager::ProcessPriorityManager()
   : mHoldsCPUWakeLock(false)
   , mHoldsHighPriorityWakeLock(false)
   , mProcessPriority(ProcessPriority(-1))
+  , mObservedTabChildCreated(false)
 {
   // When our parent process forked us, it may have set our process's priority
   // to one of a few of the process priorities, depending on exactly why this
   // process was created.
   //
   // We don't know which priority we were given, so we set mProcessPriority to
   // -1 so that the next time ResetPriorityNow is run, we'll definitely call
   // into hal and set our priority.
@@ -264,16 +283,17 @@ ProcessPriorityManager::Init()
   // We can't do this in the constructor because we need to hold a strong ref
   // to |this| before calling these methods.
   //
   // Notice that we track /window/ creation and destruction even though our
   // notion of "is-foreground" is tied to /docshell/ activity.  We do this
   // because docshells don't fire an event when their visibility changes, but
   // windows do.
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  os->AddObserver(this, "tab-child-created", /* ownsWeak = */ false);
   os->AddObserver(this, "content-document-global-created", /* ownsWeak = */ false);
   os->AddObserver(this, "inner-window-destroyed", /* ownsWeak = */ false);
   os->AddObserver(this, "audio-channel-agent-changed", /* ownsWeak = */ false);
   os->AddObserver(this, "process-priority:reset-now", /* ownsWeak = */ false);
 
   RegisterWakeLockObserver(this);
 
   // This process may already hold the CPU lock; for example, our parent may
@@ -290,17 +310,20 @@ ProcessPriorityManager::Init()
 }
 
 NS_IMETHODIMP
 ProcessPriorityManager::Observe(
   nsISupports* aSubject,
   const char* aTopic,
   const PRUnichar* aData)
 {
-  if (!strcmp(aTopic, "content-document-global-created")) {
+  if (!strcmp(aTopic, "tab-child-created")) {
+    mObservedTabChildCreated = true;
+    ResetPriority();
+  } else if (!strcmp(aTopic, "content-document-global-created")) {
     OnContentDocumentGlobalCreated(aSubject);
   } else if (!strcmp(aTopic, "inner-window-destroyed") ||
              !strcmp(aTopic, "audio-channel-agent-changed")) {
     ResetPriority();
   } else if (!strcmp(aTopic, "process-priority:reset-now")) {
     LOG("Got process-priority:reset-now notification.");
     ResetPriorityNow();
   } else {
@@ -445,32 +468,44 @@ ProcessPriorityManager::GetBackgroundPri
          PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
          PROCESS_PRIORITY_BACKGROUND;
 }
 
 
 void
 ProcessPriorityManager::ResetPriority()
 {
+  if (!mObservedTabChildCreated) {
+    LOG("ResetPriority bailing because we haven't observed "
+        "a tab-child-created event.");
+    return;
+  }
+
   if (ComputeIsInForeground()) {
     SetIsForeground();
   } else if (IsBackgroundPriority(mProcessPriority)) {
     // If we're already in the background, recompute our background priority
     // and set it immediately.
     SetIsBackgroundNow();
   } else {
     ScheduleResetPriority("backgroundGracePeriodMS");
   }
 }
 
 void
 ProcessPriorityManager::ResetPriorityNow()
 {
+  if (!mObservedTabChildCreated) {
+    LOG("ResetPriorityNow bailing because we haven't observed "
+        "a tab-child-created event.");
+    return;
+  }
+
   if (ComputeIsInForeground()) {
-    SetIsForeground();
+    SetIsForegroundNow();
   } else {
     SetIsBackgroundNow();
   }
 }
 
 bool
 ProcessPriorityManager::ComputeIsInForeground()
 {
@@ -523,16 +558,32 @@ ProcessPriorityManager::ComputeIsInForeg
 
   return !allHidden;
 }
 
 void
 ProcessPriorityManager::SetIsForeground()
 {
   ProcessPriority foregroundPriority = GetForegroundPriority();
+
+  if (mProcessPriority == PROCESS_PRIORITY_UNKNOWN ||
+      foregroundPriority < mProcessPriority) {
+    LOG("Giving grace period to %s -> %s transition.",
+        ProcessPriorityToString(mProcessPriority),
+        ProcessPriorityToString(foregroundPriority));
+    ScheduleResetPriority("backgroundGracePeriodMS");
+  } else {
+    SetIsForegroundNow();
+  }
+}
+
+void
+ProcessPriorityManager::SetIsForegroundNow()
+{
+  ProcessPriority foregroundPriority = GetForegroundPriority();
   if (foregroundPriority == mProcessPriority) {
     return;
   }
 
   // Cancel the memory minimization procedure we might have started.
   nsCOMPtr<nsICancelableRunnable> runnable =
     do_QueryReferent(mMemoryMinimizerRunnable);
   if (runnable) {