Bug 842679 - Part 3: Wait for a tab-child to be created before modifying priorities in the ProcessPriorityManager. r=cjones, a=tef+
authorJustin Lebar <justin.lebar@gmail.com>
Fri, 22 Feb 2013 23:24:28 -0500
changeset 118601 5605f2e2e94e129f84f1dfbc91e70516525b17cd
parent 118600 e4aae60246469c9df2f779d0fe6d716490486288
child 118602 aa38919cb03850ce5bb9dbe94ac3c8d16cbb3c25
push id490
push userryanvm@gmail.com
push dateMon, 25 Feb 2013 17:26:56 +0000
reviewerscjones, tef
bugs842679
milestone18.0
Bug 842679 - Part 3: Wait for a tab-child to be created before modifying priorities in the ProcessPriorityManager. r=cjones, a=tef+
dom/ipc/ProcessPriorityManager.cpp
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -149,25 +149,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();
 
@@ -190,21 +196,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();
 
   /**
@@ -219,31 +233,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.
@@ -257,16 +276,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
@@ -283,17 +303,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 {
@@ -438,32 +461,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()
 {
@@ -520,16 +555,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) {