Bug 892371 - Adjust oom_score_adj values for foreground processes according to an LRU policy. r=dhylands, r=khuey
authorGabriele Svelto <gsvelto@mozilla.com>
Wed, 25 Feb 2015 09:37:38 +0100
changeset 251638 908eaacff6f499831472cfb923d91afd8b2e3d0b
parent 251637 688fdf0758a281ffabd633c94f0b0c256a65ca91
child 251639 a30c985bc8dd336683b1b65d672b08577909f89d
push id7860
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:46:02 +0000
treeherdermozilla-aurora@8ac636cd51f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands, khuey
bugs892371
milestone39.0a1
Bug 892371 - Adjust oom_score_adj values for foreground processes according to an LRU policy. r=dhylands, r=khuey
b2g/app/b2g.js
dom/browser-element/mochitest/browserElementTestHelpers.js
dom/browser-element/mochitest/priority/test_BackgroundLRU.html
dom/ipc/ProcessPriorityManager.cpp
dom/ipc/ProcessPriorityManager.h
dom/ipc/tests/test_NuwaProcessCreation.html
dom/ipc/tests/test_NuwaProcessDeadlock.html
hal/Hal.cpp
hal/fallback/FallbackProcessPriority.cpp
hal/gonk/GonkHal.cpp
hal/sandbox/SandboxHal.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -698,20 +698,21 @@ pref("layout.scrollbar.side", 1);
 // documents a 1s grace period before they're eligible to be marked as
 // background. Background processes that are perceivable due to playing
 // media are given a longer grace period to accomodate changing tracks, etc.
 pref("dom.ipc.processPriorityManager.enabled", true);
 pref("dom.ipc.processPriorityManager.backgroundGracePeriodMS", 1000);
 pref("dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS", 5000);
 pref("dom.ipc.processPriorityManager.temporaryPriorityLockMS", 5000);
 
-// Number of different background levels for background processes.  We use
-// these different levels to force the low-memory killer to kill processes in
-// a LRU order.
-pref("dom.ipc.processPriorityManager.backgroundLRUPoolLevels", 5);
+// Number of different background/foreground levels for background/foreground
+// processes.  We use these different levels to force the low-memory killer to
+// kill processes in a LRU order.
+pref("dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels", 5);
+pref("dom.ipc.processPriorityManager.FOREGROUND.LRUPoolLevels", 3);
 
 // Kernel parameters for process priorities.  These affect how processes are
 // killed on low-memory and their relative CPU priorities.
 //
 // The kernel can only accept 6 (OomScoreAdjust, KillUnderKB) pairs. But it is
 // okay, kernel will still kill processes with larger OomScoreAdjust first even
 // its OomScoreAdjust don't have a corresponding KillUnderKB.
 
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -45,19 +45,19 @@ const browserElementTestHelpers = {
     if (this._testReadyLockCount == 0 && !this._firedTestReady) {
       this._firedTestReady = true;
       dispatchEvent(new Event("testready"));
     }
   },
 
   enableProcessPriorityManager: function() {
     this._setPrefs(
+      ['dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2],
       ['dom.ipc.processPriorityManager.testMode', true],
-      ['dom.ipc.processPriorityManager.enabled', true],
-      ['dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2]
+      ['dom.ipc.processPriorityManager.enabled', true]
     );
   },
 
   setEnabledPref: function(value) {
     this._setPref('dom.mozBrowserFramesEnabled', value);
   },
 
   setSelectionChangeEnabledPref: function(value) {
@@ -182,38 +182,38 @@ function expectPriorityChange(childID, e
         } else {
           reject();
         }
       }
     );
   });
 }
 
-// Returns a promise which is resolved or rejected the next time the background
-// process childID changes its priority.  We resolve if the backgroundLRU
-// matches expectedBackgroundLRU and we reject otherwise.
+// Returns a promise which is resolved or rejected the next time the
+// process childID changes its priority.  We resolve if the LRU parameter
+// matches expectedLRU and we reject otherwise.
 
-function expectPriorityWithBackgroundLRUSet(childID, expectedBackgroundLRU) {
+function expectPriorityWithLRUSet(childID, expectedLRU) {
   return new Promise(function(resolve, reject) {
 
     browserElementTestHelpers.addProcessPriorityObserver(
-      'process-priority-with-background-LRU-set',
+      'process-priority-with-LRU-set',
       function(subject, topic, data) {
 
-        var [id, priority, backgroundLRU] = data.split(":");
+        var [id, priority, lru] = data.split(":");
         if (id != childID) {
           return;
         }
 
-        is(backgroundLRU, expectedBackgroundLRU,
-           'Expected backgroundLRU ' + backgroundLRU +
+        is(lru, expectedLRU,
+           'Expected LRU ' + lru +
            ' of childID ' + childID +
-           ' to change to ' + expectedBackgroundLRU);
+           ' to change to ' + expectedLRU);
 
-        if (backgroundLRU == expectedBackgroundLRU) {
+        if (lru == expectedLRU) {
           resolve();
         } else {
           reject();
         }
       }
     );
   });
 }
--- a/dom/browser-element/mochitest/priority/test_BackgroundLRU.html
+++ b/dom/browser-element/mochitest/priority/test_BackgroundLRU.html
@@ -44,17 +44,17 @@ function runTest() {
     iframe2.src = browserElementTestHelpers.emptyPage1;
 
     document.body.appendChild(iframe2);
 
     // At this point, we should have iframe1 in background already.
     // We wait until another one is set to background, too.
     // Once there are two in background, the first one (LRU order)
     // should have 'backgroundLRU' equals 1
-    var p = expectPriorityWithBackgroundLRUSet(childID, '1');
+    var p = expectPriorityWithLRUSet(childID, '1');
     iframe2.setVisible(false);
 
     return p;
 
   }).then(function() {
     // Don't call removeChild immediately after calling setVisible.
     // setVisible on remote browser is async method, so we should wait
     // until it sends to the child process.
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProcessPriorityManager.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/Hal.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "AudioChannelService.h"
 #include "prlog.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 #include "nsIFrameLoader.h"
@@ -49,58 +50,95 @@
 #  include <android/log.h>
 #  define LOG(fmt, ...) \
      __android_log_print(ANDROID_LOG_INFO, \
        "Gecko:ProcessPriorityManager", \
        fmt, ## __VA_ARGS__)
 #  define LOGP(fmt, ...) \
     __android_log_print(ANDROID_LOG_INFO, \
       "Gecko:ProcessPriorityManager", \
-      "[%schild-id=%llu, pid=%d] " fmt, \
+      "[%schild-id=%" PRIu64 ", pid=%d] " fmt, \
       NameWithComma().get(), \
-      (long long unsigned) ChildID(), Pid(), ## __VA_ARGS__)
+      static_cast<uint64_t>(ChildID()), Pid(), ## __VA_ARGS__)
 
 #elif defined(ENABLE_LOGGING)
 #  define LOG(fmt, ...) \
      printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__)
 #  define LOGP(fmt, ...) \
-     printf("ProcessPriorityManager[%schild-id=%llu, pid=%d] - " fmt "\n", \
+     printf("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " \
+       fmt "\n", \
        NameWithComma().get(), \
-       (unsigned long long) ChildID(), Pid(), ##__VA_ARGS__)
+       static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__)
 
 #elif defined(PR_LOGGING)
   static PRLogModuleInfo*
   GetPPMLog()
   {
     static PRLogModuleInfo *sLog;
     if (!sLog)
       sLog = PR_NewLogModule("ProcessPriorityManager");
     return sLog;
   }
 #  define LOG(fmt, ...) \
      PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
             ("ProcessPriorityManager - " fmt, ##__VA_ARGS__))
 #  define LOGP(fmt, ...) \
      PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
-            ("ProcessPriorityManager[%schild-id=%llu, pid=%d] - " fmt, \
+            ("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " fmt, \
             NameWithComma().get(), \
-            (unsigned long long) ChildID(), Pid(), ##__VA_ARGS__))
+            static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__))
 #else
 #define LOG(fmt, ...)
 #define LOGP(fmt, ...)
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 namespace {
 
 class ParticularProcessPriorityManager;
 
+class ProcessLRUPool MOZ_FINAL
+{
+public:
+  /**
+   * Creates a new process LRU pool for the specified priority.
+   */
+  ProcessLRUPool(ProcessPriority aPriority, uint32_t aBias);
+
+  /**
+   * Used to remove a particular process priority manager from the LRU pool
+   * when the associated ContentParent is destroyed or its priority changes.
+   */
+  void Remove(ParticularProcessPriorityManager* aParticularManager);
+
+  /**
+   * Used to add a particular process priority manager into the LRU pool when
+   * the associated ContentParent's priority changes.
+   */
+  void Add(ParticularProcessPriorityManager* aParticularManager);
+
+private:
+  ProcessPriority mPriority;
+  uint32_t mLRUPoolLevels;
+  uint32_t mLRUPoolSize;
+  uint32_t mBias;
+  nsTArray<ParticularProcessPriorityManager*> mLRUPool;
+
+  uint32_t CalculateLRULevel(uint32_t aLRUPoolIndex);
+
+  void AdjustLRUValues(
+    nsTArray<ParticularProcessPriorityManager*>::index_type aStart,
+    bool removed);
+
+  DISALLOW_EVIL_CONSTRUCTORS(ProcessLRUPool);
+};
+
 /**
  * This singleton class does the work to implement the process priority manager
  * in the main process.  This class may not be used in child processes.  (You
  * can call StaticInit, but it won't do anything, and GetSingleton() will
  * return null.)
  *
  * ProcessPriorityManager::CurrentProcessIsForeground() and
  * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in
@@ -124,17 +162,17 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   /**
    * This function implements ProcessPriorityManager::SetProcessPriority.
    */
   void SetProcessPriority(ContentParent* aContentParent,
                           ProcessPriority aPriority,
-                          uint32_t aBackgroundLRU = 0);
+                          uint32_t aLRU = 0);
 
   /**
    * If a magic testing-only pref is set, notify the observer service on the
    * given topic with the given data.  This is used for testing
    */
   void FireTestOnlyObserverNotification(const char* aTopic,
                                         const nsACString& aData = EmptyCString());
 
@@ -174,18 +212,27 @@ private:
   GetParticularProcessPriorityManager(ContentParent* aContentParent);
 
   void ObserveContentParentCreated(nsISupports* aContentParent);
   void ObserveContentParentDestroyed(nsISupports* aSubject);
 
   nsDataHashtable<nsUint64HashKey, nsRefPtr<ParticularProcessPriorityManager> >
     mParticularManagers;
 
+  /** True if the main process is holding a high-priority wakelock */
   bool mHighPriority;
+
+  /** Contains the PIDs of child processes holding high-priority wakelocks */
   nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
+
+  /** Contains a pseudo-LRU list of background processes */
+  ProcessLRUPool mBackgroundLRUPool;
+
+  /** Contains a pseudo-LRU list of foreground processes */
+  ProcessLRUPool mForegroundLRUPool;
 };
 
 /**
  * This singleton class implements the parts of the process priority manager
  * that are available from all processes.
  */
 class ProcessPriorityManagerChild MOZ_FINAL
   : public nsIObserver
@@ -257,83 +304,44 @@ public:
   void OnFrameloaderVisibleChanged(nsISupports* aSubject);
 
   ProcessPriority CurrentPriority();
   ProcessPriority ComputePriority();
 
   void ScheduleResetPriority(const char* aTimeoutPref);
   void ResetPriority();
   void ResetPriorityNow();
-  void SetPriorityNow(ProcessPriority aPriority, uint32_t aBackgroundLRU = 0);
+  void SetPriorityNow(ProcessPriority aPriority, uint32_t aLRU = 0);
 
   void ShutDown();
 
 private:
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const nsACString& aData = EmptyCString());
 
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const char* aData = nullptr);
 
   ContentParent* mContentParent;
   uint64_t mChildID;
   ProcessPriority mPriority;
+  uint32_t mLRU;
   bool mHoldsCPUWakeLock;
   bool mHoldsHighPriorityWakeLock;
 
   /**
    * Used to implement NameWithComma().
    */
   nsAutoCString mNameWithComma;
 
   nsCOMPtr<nsITimer> mResetPriorityTimer;
 };
 
-class BackgroundProcessLRUPool MOZ_FINAL
-{
-public:
-  static BackgroundProcessLRUPool* Singleton();
-
-  /**
-   * Used to remove a ContentParent from background LRU pool when
-   * it is destroyed or its priority changed from BACKGROUND to others.
-   */
-  void RemoveFromBackgroundLRUPool(ContentParent* aContentParent);
-
-  /**
-   * Used to add a ContentParent into background LRU pool when
-   * its priority changed to BACKGROUND from others.
-   */
-  void AddIntoBackgroundLRUPool(ContentParent* aContentParent);
-
-private:
-  static StaticAutoPtr<BackgroundProcessLRUPool> sSingleton;
-
-  int32_t mLRUPoolLevels;
-  int32_t mLRUPoolSize;
-  int32_t mLRUPoolAvailableIndex;
-  nsTArray<ContentParent*> mLRUPool;
-
-  uint32_t CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex);
-
-  nsresult UpdateAvailableIndexInLRUPool(
-      ContentParent* aContentParent,
-      int32_t aTargetIndex = -1);
-
-  void ShiftLRUPool();
-
-  void EnsureLRUPool();
-
-  BackgroundProcessLRUPool();
-  DISALLOW_EVIL_CONSTRUCTORS(BackgroundProcessLRUPool);
-
-};
-
 /* static */ bool ProcessPriorityManagerImpl::sInitialized = false;
 /* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
 /* static */ StaticRefPtr<ProcessPriorityManagerImpl>
   ProcessPriorityManagerImpl::sSingleton;
 
 NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl,
                   nsIObserver);
 
@@ -394,16 +402,18 @@ ProcessPriorityManagerImpl::GetSingleton
     StaticInit();
   }
 
   return sSingleton;
 }
 
 ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
     : mHighPriority(false)
+    , mBackgroundLRUPool(PROCESS_PRIORITY_BACKGROUND, 1)
+    , mForegroundLRUPool(PROCESS_PRIORITY_FOREGROUND, 0)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   RegisterWakeLockObserver(this);
 }
 
 ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl()
 {
   UnregisterWakeLockObserver(this);
@@ -468,23 +478,23 @@ ProcessPriorityManagerImpl::GetParticula
   }
 
   return pppm.forget();
 }
 
 void
 ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
                                                ProcessPriority aPriority,
-                                               uint32_t aBackgroundLRU)
+                                               uint32_t aLRU)
 {
   MOZ_ASSERT(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(aContentParent);
   if (pppm) {
-    pppm->SetPriorityNow(aPriority, aBackgroundLRU);
+    pppm->SetPriorityNow(aPriority, aLRU);
   }
 }
 
 void
 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   nsISupports* aContentParent)
 {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
@@ -502,16 +512,20 @@ ProcessPriorityManagerImpl::ObserveConte
 
   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
 
   nsRefPtr<ParticularProcessPriorityManager> pppm;
   mParticularManagers.Get(childID, &pppm);
   if (pppm) {
+    // Unconditionally remove the manager from the pools
+    mBackgroundLRUPool.Remove(pppm);
+    mForegroundLRUPool.Remove(pppm);
+
     pppm->ShutDown();
 
     mParticularManagers.Remove(childID);
 
     if (mHighPriorityChildIDs.Contains(childID)) {
       mHighPriorityChildIDs.RemoveEntry(childID);
     }
   }
@@ -523,31 +537,42 @@ ProcessPriorityManagerImpl::ChildProcess
   return mHighPriorityChildIDs.Count() > 0;
 }
 
 void
 ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
   ParticularProcessPriorityManager* aParticularManager,
   ProcessPriority aOldPriority)
 {
-  /* We're interested only in changes to/from FOREGROUND_HIGH as we use we
-   * need to track high priority processes so that we can react to their
-   * presence. */
+  ProcessPriority newPriority = aParticularManager->CurrentPriority();
+  bool isPreallocated = aParticularManager->IsPreallocated();
 
-  if (aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
-      aParticularManager->CurrentPriority() <
-        PROCESS_PRIORITY_FOREGROUND_HIGH) {
-
-    return;
+  if (newPriority == PROCESS_PRIORITY_BACKGROUND &&
+      aOldPriority != PROCESS_PRIORITY_BACKGROUND &&
+      !isPreallocated) {
+    mBackgroundLRUPool.Add(aParticularManager);
+  } else if (newPriority != PROCESS_PRIORITY_BACKGROUND &&
+      aOldPriority == PROCESS_PRIORITY_BACKGROUND &&
+      !isPreallocated) {
+    mBackgroundLRUPool.Remove(aParticularManager);
   }
 
-  if (aParticularManager->CurrentPriority() >=
-      PROCESS_PRIORITY_FOREGROUND_HIGH) {
+  if (newPriority == PROCESS_PRIORITY_FOREGROUND &&
+      aOldPriority != PROCESS_PRIORITY_FOREGROUND) {
+    mForegroundLRUPool.Add(aParticularManager);
+  } else if (newPriority != PROCESS_PRIORITY_FOREGROUND &&
+      aOldPriority == PROCESS_PRIORITY_FOREGROUND) {
+    mForegroundLRUPool.Remove(aParticularManager);
+  }
+
+  if (newPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH &&
+    aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH) {
     mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID());
-  } else {
+  } else if (newPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
+    aOldPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
     mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
   }
 }
 
 /* virtual */ void
 ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
 {
   /* The main process always has an ID of 0, if it is present in the wake-lock
@@ -570,16 +595,17 @@ NS_IMPL_ISUPPORTS(ParticularProcessPrior
                   nsITimerCallback,
                   nsISupportsWeakReference);
 
 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   ContentParent* aContentParent)
   : mContentParent(aContentParent)
   , mChildID(aContentParent->ChildID())
   , mPriority(PROCESS_PRIORITY_UNKNOWN)
+  , mLRU(0)
   , mHoldsCPUWakeLock(false)
   , mHoldsHighPriorityWakeLock(false)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   LOGP("Creating ParticularProcessPriorityManager.");
 }
 
 void
@@ -932,59 +958,39 @@ ParticularProcessPriorityManager::Comput
 
   return HasAppType("homescreen") ?
          PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
          PROCESS_PRIORITY_BACKGROUND;
 }
 
 void
 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
-                                                 uint32_t aBackgroundLRU)
+                                                 uint32_t aLRU)
 {
   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
     MOZ_ASSERT(false);
     return;
   }
 
-  if (aBackgroundLRU > 0 &&
-      aPriority == PROCESS_PRIORITY_BACKGROUND &&
-      mPriority == PROCESS_PRIORITY_BACKGROUND) {
-    hal::SetProcessPriority(Pid(), mPriority, aBackgroundLRU);
-
-    nsPrintfCString ProcessPriorityWithBackgroundLRU("%s:%d",
-      ProcessPriorityToString(mPriority), aBackgroundLRU);
-
-    FireTestOnlyObserverNotification("process-priority-with-background-LRU-set",
-      ProcessPriorityWithBackgroundLRU.get());
-  }
-
-  if (!mContentParent ||
-      !ProcessPriorityManagerImpl::PrefsEnabled() ||
-      (mPriority == aPriority)) {
+  if (!ProcessPriorityManagerImpl::PrefsEnabled() ||
+      !mContentParent ||
+      ((mPriority == aPriority) && (mLRU == aLRU))) {
     return;
   }
 
-  // If the prefs were disabled after this ParticularProcessPriorityManager was
-  // created, we can at least avoid any further calls to
-  // hal::SetProcessPriority.  Supporting dynamic enabling/disabling of the
-  // ProcessPriorityManager is mostly for testing.
-  if (!ProcessPriorityManagerImpl::PrefsEnabled()) {
-    return;
-  }
+  if ((mPriority == aPriority) && (mLRU != aLRU)) {
+    mLRU = aLRU;
+    hal::SetProcessPriority(Pid(), mPriority, aLRU);
 
-  if (aPriority == PROCESS_PRIORITY_BACKGROUND &&
-      mPriority != PROCESS_PRIORITY_BACKGROUND &&
-      !IsPreallocated()) {
-    ProcessPriorityManager::AddIntoBackgroundLRUPool(mContentParent);
-  }
+    nsPrintfCString processPriorityWithLRU("%s:%d",
+      ProcessPriorityToString(mPriority), aLRU);
 
-  if (aPriority != PROCESS_PRIORITY_BACKGROUND &&
-      mPriority == PROCESS_PRIORITY_BACKGROUND &&
-      !IsPreallocated()) {
-    ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
+    FireTestOnlyObserverNotification("process-priority-with-LRU-set",
+      processPriorityWithLRU.get());
+    return;
   }
 
   LOGP("Changing priority from %s to %s.",
        ProcessPriorityToString(mPriority),
        ProcessPriorityToString(aPriority));
 
   ProcessPriority oldPriority = mPriority;
 
@@ -1013,20 +1019,16 @@ ParticularProcessPriorityManager::ShutDo
 
   UnregisterWakeLockObserver(this);
 
   if (mResetPriorityTimer) {
     mResetPriorityTimer->Cancel();
     mResetPriorityTimer = nullptr;
   }
 
-  if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) {
-    ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
-  }
-
   mContentParent = nullptr;
 }
 
 void
 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
   const char* aTopic,
   const nsACString& aData /* = EmptyCString() */)
 {
@@ -1157,189 +1159,122 @@ ProcessPriorityManagerChild::CurrentProc
 
 bool
 ProcessPriorityManagerChild::CurrentProcessIsHighPriority()
 {
   return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
          mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH;
 }
 
-/* static */ StaticAutoPtr<BackgroundProcessLRUPool>
-BackgroundProcessLRUPool::sSingleton;
-
-/* static */ BackgroundProcessLRUPool*
-BackgroundProcessLRUPool::Singleton()
+ProcessLRUPool::ProcessLRUPool(ProcessPriority aPriority, uint32_t aBias)
+  : mPriority(aPriority)
+  , mLRUPoolLevels(1)
+  , mBias(aBias)
 {
-  if (!sSingleton) {
-    sSingleton = new BackgroundProcessLRUPool();
-    ClearOnShutdown(&sSingleton);
-  }
-  return sSingleton;
-}
-
-BackgroundProcessLRUPool::BackgroundProcessLRUPool()
-{
-  EnsureLRUPool();
-}
-
-uint32_t
-BackgroundProcessLRUPool::CalculateLRULevel(uint32_t aBackgroundLRUPoolIndex)
-{
-  // Set LRU level of each background process and maintain LRU buffer as below:
+  // We set mLRUPoolLevels according to our pref.
+  // This value is used to set background process LRU pool
+  const char* str = ProcessPriorityToString(aPriority);
+  nsPrintfCString pref("dom.ipc.processPriorityManager.%s.LRUPoolLevels", str);
 
-  // Priority background  : LRU0
-  // Priority background+1: LRU1, LRU2
-  // Priority background+2: LRU3, LRU4, LRU5, LRU6
-  // Priority background+3: LRU7, LRU8, LRU9, LRU10, LRU11, LRU12, LRU13, LRU14
-  // ...
-  // Priority background+L-1: 2^(number of background LRU pool levels - 1)
-  // (End of buffer)
-
-  return (uint32_t)(log((float)aBackgroundLRUPoolIndex) / log(2.0));
-}
-
-void
-BackgroundProcessLRUPool::EnsureLRUPool()
-{
-  // We set mBackgroundLRUPoolLevels according to our pref.
-  // This value is used to set background process LRU pool
-  if (!NS_SUCCEEDED(Preferences::GetInt(
-        "dom.ipc.processPriorityManager.backgroundLRUPoolLevels",
-        &mLRUPoolLevels))) {
-    mLRUPoolLevels = 1;
-  }
-
-  if (mLRUPoolLevels <= 0) {
-    MOZ_CRASH();
-  }
+  Preferences::GetUint(pref.get(), &mLRUPoolLevels);
 
   // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines
   // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10.
   // This means we can only have at most (15 -10 + 1) = 6 background LRU levels.
-  // See bug 822325 comment 49
-  MOZ_ASSERT(mLRUPoolLevels <= 6);
+  // Similarly we can have at most 4 foreground LRU levels. We should really be
+  // getting rid of oom_adj and just rely on oom_score_adj only which would
+  // lift this constraint.
+  MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND || mLRUPoolLevels <= 6);
+  MOZ_ASSERT(aPriority != PROCESS_PRIORITY_FOREGROUND || mLRUPoolLevels <= 4);
 
   // LRU pool size = 2 ^ (number of background LRU pool levels) - 1
   mLRUPoolSize = (1 << mLRUPoolLevels) - 1;
 
-  mLRUPoolAvailableIndex = 0;
+  LOG("Making %s LRU pool with size(%d)", str, mLRUPoolSize);
+}
+
+uint32_t
+ProcessLRUPool::CalculateLRULevel(uint32_t aLRU)
+{
+  // This is used to compute the LRU adjustment for the specified LRU position.
+  // We use power-of-two groups with increasing adjustments that look like the
+  // following:
 
-  LOG("Making background LRU pool with size(%d)", mLRUPoolSize);
+  // Priority  : LRU0, LRU1
+  // Priority+1: LRU2, LRU3
+  // Priority+2: LRU4, LRU5, LRU6, LRU7
+  // Priority+3: LRU8, LRU9, LRU10, LRU11, LRU12, LRU12, LRU13, LRU14, LRU15
+  // ...
+  // Priority+L-1: 2^(number of LRU pool levels - 1)
+  // (End of buffer)
 
-  mLRUPool.InsertElementsAt(0, mLRUPoolSize, (ContentParent*)nullptr);
+  // Biasing the input can be used to shift the assignment
+
+  int exp;
+  unused << frexp(static_cast<double>(aLRU), &exp);
+  uint32_t level = std::max(exp - 1, 0);
+
+  return std::min(mLRUPoolLevels - 1, level);
 }
 
 void
-BackgroundProcessLRUPool::RemoveFromBackgroundLRUPool(
-    ContentParent* aContentParent)
+ProcessLRUPool::Remove(ParticularProcessPriorityManager* aParticularManager)
 {
-  for (int32_t i = 0; i < mLRUPoolSize; i++) {
-    if (mLRUPool[i]) {
-      if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
-
-        mLRUPool[i] = nullptr;
-        LOG("Remove ChildID(%llu) from LRU pool", aContentParent->ChildID());
-
-        // After we remove this ContentParent from LRU pool, we still need to
-        // update the available index if the index of removed one is less than
-        // the available index we already have.
-        UpdateAvailableIndexInLRUPool(aContentParent, i);
-        break;
-      }
-    }
-  }
-}
+  nsTArray<ParticularProcessPriorityManager*>::index_type index =
+    mLRUPool.IndexOf(aParticularManager);
 
-nsresult
-BackgroundProcessLRUPool::UpdateAvailableIndexInLRUPool(
-    ContentParent* aContentParent,
-    int32_t aTargetIndex)
-{
-  // If we specify which index we want to assign to mLRUPoolAvailableIndex,
-  // We have to make sure the index in LRUPool doesn't point to any
-  // ContentParent.
-  if (aTargetIndex >= 0 && aTargetIndex < mLRUPoolSize &&
-      aTargetIndex < mLRUPoolAvailableIndex &&
-      !mLRUPool[aTargetIndex]) {
-    mLRUPoolAvailableIndex = aTargetIndex;
-    return NS_OK;
-  }
-
-  // When we didn't specify any legal aTargetIndex, then we just check
-  // whether current mLRUPoolAvailableIndex points to any ContentParent or not.
-  if (mLRUPoolAvailableIndex >= 0 && mLRUPoolAvailableIndex < mLRUPoolSize &&
-      !(mLRUPool[mLRUPoolAvailableIndex])) {
-    return NS_OK;
+  if (index == nsTArray<ParticularProcessPriorityManager*>::NoIndex) {
+    return;
   }
 
-  // Both above way failed. So now we have to find proper value
-  // for mLRUPoolAvailableIndex.
-  // We are looking for an available index. We only shift process with
-  // LRU less than the available index should have, so we stop update
-  // mLRUPoolAvailableIndex from the for loop once we got a candidate.
-  mLRUPoolAvailableIndex = -1;
+  mLRUPool.RemoveElementAt(index);
+  AdjustLRUValues(index, /* removed */ true);
 
-  for (int32_t i = 0; i < mLRUPoolSize; i++) {
-    if (mLRUPool[i]) {
-      if (mLRUPool[i]->ChildID() == aContentParent->ChildID()) {
-        LOG("ChildID(%llu) already in LRU pool", aContentParent->ChildID());
-        MOZ_ASSERT(false);
-        return NS_ERROR_UNEXPECTED;
-      }
-      continue;
-    } else {
-      if (mLRUPoolAvailableIndex == -1) {
-        mLRUPoolAvailableIndex = i;
-      }
-    }
-  }
-
-  // If the LRUPool is already full, mLRUPoolAvailableIndex is still -1 after
-  // above loop finished. We should set mLRUPoolAvailableIndex
-  // to mLRUPoolSize - 1 in this case. Here uses the mod operator to do it:
-  // New mLRUPoolAvailableIndex either equals old mLRUPoolAvailableIndex, or
-  // mLRUPoolSize - 1 if old mLRUPoolAvailableIndex is -1.
-  mLRUPoolAvailableIndex =
-    (mLRUPoolAvailableIndex + mLRUPoolSize) % mLRUPoolSize;
-
-  return NS_OK;
+  LOG("Remove ChildID(%" PRIu64 ") from %s LRU pool",
+      static_cast<uint64_t>(aParticularManager->ChildID()),
+      ProcessPriorityToString(mPriority));
 }
 
+/*
+ * Adjust the LRU values of all the processes in an LRU pool. When true the
+ * `removed` parameter indicates that the processes were shifted left because
+ * an element was removed; otherwise it means the elements were shifted right
+ * as an element was added.
+ */
 void
-BackgroundProcessLRUPool::ShiftLRUPool()
+ProcessLRUPool::AdjustLRUValues(
+  nsTArray<ParticularProcessPriorityManager*>::index_type aStart,
+  bool removed)
 {
-  for (int32_t i = mLRUPoolAvailableIndex; i > 0; i--) {
-    mLRUPool[i] = mLRUPool[i - 1];
-    // Check whether i+1 is power of Two.
-    // If so, then it crossed a LRU group boundary and
-    // we need to assign its new process priority LRU.
-    if (!((i + 1) & i)) {
-      ProcessPriorityManagerImpl::GetSingleton()->SetProcessPriority(
-        mLRUPool[i], PROCESS_PRIORITY_BACKGROUND, CalculateLRULevel(i + 1));
+  uint32_t adj = (removed ? 1 : 0) + mBias;
+
+  for (nsTArray<ParticularProcessPriorityManager*>::index_type i = aStart;
+       i < mLRUPool.Length();
+       i++) {
+    /* Check whether i is a power of two.  If so, then it crossed a LRU group
+     * boundary and we need to assign its new process priority LRU. Note that
+     * depending on the direction and the bias this test will pick different
+     * elements. */
+    if (((i + adj) & (i + adj - 1)) == 0) {
+      mLRUPool[i]->SetPriorityNow(mPriority, CalculateLRULevel(i + mBias));
     }
   }
 }
 
 void
-BackgroundProcessLRUPool::AddIntoBackgroundLRUPool(
-    ContentParent* aContentParent)
+ProcessLRUPool::Add(ParticularProcessPriorityManager* aParticularManager)
 {
-  // We have to make sure that we have correct available index in LRU pool
-  if (!NS_SUCCEEDED(
-      UpdateAvailableIndexInLRUPool(aContentParent))) {
-    return;
-  }
+  // Shift the list in the pool, so we have room at index 0 for the newly added
+  // manager
+  mLRUPool.InsertElementAt(0, aParticularManager);
+  AdjustLRUValues(1, /* removed */ false);
 
-  // Shift the list in the pool, so we have room at index 0 for the newly added
-  // ContentParent
-  ShiftLRUPool();
-
-  mLRUPool[0] = aContentParent;
-
-  LOG("Add ChildID(%llu) into LRU pool", aContentParent->ChildID());
+  LOG("Add ChildID(%" PRIu64 ") into %s LRU pool",
+      static_cast<uint64_t>(aParticularManager->ChildID()),
+      ProcessPriorityToString(mPriority));
 }
 
 } // anonymous namespace
 
 namespace mozilla {
 
 /* static */ void
 ProcessPriorityManager::Init()
@@ -1356,41 +1291,16 @@ ProcessPriorityManager::SetProcessPriori
 
   ProcessPriorityManagerImpl* singleton =
     ProcessPriorityManagerImpl::GetSingleton();
   if (singleton) {
     singleton->SetProcessPriority(aContentParent, aPriority);
   }
 }
 
-/* static */ void
-ProcessPriorityManager::RemoveFromBackgroundLRUPool(
-    ContentParent* aContentParent)
-{
-  MOZ_ASSERT(aContentParent);
-
-  BackgroundProcessLRUPool* singleton =
-    BackgroundProcessLRUPool::Singleton();
-  if (singleton) {
-    singleton->RemoveFromBackgroundLRUPool(aContentParent);
-  }
-}
-
-/* static */ void
-ProcessPriorityManager::AddIntoBackgroundLRUPool(ContentParent* aContentParent)
-{
-  MOZ_ASSERT(aContentParent);
-
-  BackgroundProcessLRUPool* singleton =
-    BackgroundProcessLRUPool::Singleton();
-  if (singleton) {
-    singleton->AddIntoBackgroundLRUPool(aContentParent);
-  }
-}
-
 /* static */ bool
 ProcessPriorityManager::CurrentProcessIsForeground()
 {
   return ProcessPriorityManagerChild::Singleton()->
     CurrentProcessIsForeground();
 }
 
 /* static */ bool
--- a/dom/ipc/ProcessPriorityManager.h
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -69,28 +69,16 @@ public:
   static bool CurrentProcessIsForeground();
 
   /**
    * Returns true if one or more processes with FOREGROUND_HIGH priority are
    * present, false otherwise.
    */
   static bool AnyProcessHasHighPriority();
 
-  /**
-   * Used to remove a ContentParent from background LRU pool when
-   * it is destroyed or its priority changed from BACKGROUND to others.
-   */
-  static void RemoveFromBackgroundLRUPool(dom::ContentParent* aContentParent);
-
-  /**
-   * Used to add a ContentParent into background LRU pool when
-   * its priority changed to BACKGROUND from others.
-   */
-  static void AddIntoBackgroundLRUPool(dom::ContentParent* aContentParent);
-
 private:
   ProcessPriorityManager();
   DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManager);
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/ipc/tests/test_NuwaProcessCreation.html
+++ b/dom/ipc/tests/test_NuwaProcessCreation.html
@@ -46,17 +46,17 @@ function setPref(pref, value) {
     SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, function() { testLoader.unlockTestReady(); });
   } else {
     SpecialPowers.pushPrefEnv({'clear': [[pref]]}, function() { testLoader.unlockTestReady(); });
   }
 }
 
 setPref('dom.ipc.processPriorityManager.testMode', true);
 setPref('dom.ipc.processPriorityManager.enabled', true);
-setPref('dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2);
+setPref('dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2);
 
 function runTest()
 {
   // Shutdown preallocated process.
   SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', false);
   let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
                           .getService(SpecialPowers.Ci.nsISyncMessageSender);
   let seenNuwaReady = false;
--- a/dom/ipc/tests/test_NuwaProcessDeadlock.html
+++ b/dom/ipc/tests/test_NuwaProcessDeadlock.html
@@ -46,17 +46,17 @@ function setPref(pref, value) {
     SpecialPowers.pushPrefEnv({'set': [[pref, value]]}, function() { testLoader.unlockTestReady(); });
   } else {
     SpecialPowers.pushPrefEnv({'clear': [[pref]]}, function() { testLoader.unlockTestReady(); });
   }
 }
 
 setPref('dom.ipc.processPriorityManager.testMode', true);
 setPref('dom.ipc.processPriorityManager.enabled', true);
-setPref('dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2);
+setPref('dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2);
 setPref('dom.ipc.processPrelaunch.testMode', true);  // For testing deadlock.
 
 function runTest()
 {
   // Shutdown preallocated process.
   SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', false);
   let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
                           .getService(SpecialPowers.Ci.nsISyncMessageSender);
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -856,24 +856,21 @@ bool
 SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
 {
   // It's pointless to program an alarm nothing is going to observe ...
   MOZ_ASSERT(sAlarmObserver);
   RETURN_PROXY_IF_SANDBOXED(SetAlarm(aSeconds, aNanoseconds), false);
 }
 
 void
-SetProcessPriority(int aPid,
-                   ProcessPriority aPriority,
-                   uint32_t aBackgroundLRU)
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
 {
   // n.b. The sandboxed implementation crashes; SetProcessPriority works only
   // from the main process.
-  MOZ_ASSERT(aBackgroundLRU == 0 || aPriority == PROCESS_PRIORITY_BACKGROUND);
-  PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority, aBackgroundLRU));
+  PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority, aLRU));
 }
 
 void
 SetCurrentThreadPriority(hal::ThreadPriority aThreadPriority)
 {
   PROXY_IF_SANDBOXED(SetCurrentThreadPriority(aThreadPriority));
 }
 
--- a/hal/fallback/FallbackProcessPriority.cpp
+++ b/hal/fallback/FallbackProcessPriority.cpp
@@ -6,18 +6,16 @@
 #include "HalLog.h"
 
 using namespace mozilla::hal;
 
 namespace mozilla {
 namespace hal_impl {
 
 void
-SetProcessPriority(int aPid,
-                   ProcessPriority aPriority,
-                   uint32_t aBackgroundLRU)
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
 {
   HAL_LOG("FallbackProcessPriority - SetProcessPriority(%d, %s, %u)\n",
-          aPid, ProcessPriorityToString(aPriority), aBackgroundLRU);
+          aPid, ProcessPriorityToString(aPriority), aLRU);
 }
 
 } // hal_impl
 } // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -1194,22 +1194,22 @@ OomAdjOfOomScoreAdj(int aOomScoreAdj)
   } else {
     adj = (OOM_ADJUST_MAX * aOomScoreAdj) / OOM_SCORE_ADJ_MAX;
   }
 
   return adj;
 }
 
 static void
-RoundOomScoreAdjUpWithBackroundLRU(int& aOomScoreAdj, uint32_t aBackgroundLRU)
+RoundOomScoreAdjUpWithLRU(int& aOomScoreAdj, uint32_t aLRU)
 {
   // We want to add minimum value to round OomScoreAdj up according to
-  // the steps by aBackgroundLRU.
+  // the steps by aLRU.
   aOomScoreAdj +=
-    ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aBackgroundLRU);
+    ceil(((float)OOM_SCORE_ADJ_MAX / OOM_ADJUST_MAX) * aLRU);
 }
 
 #define OOM_LOG(level, args...) __android_log_print(level, "OomLogger", ##args)
 class OomVictimLogger MOZ_FINAL
   : public nsIObserver
 {
 public:
   OomVictimLogger()
@@ -1785,37 +1785,35 @@ EnsureKernelLowMemKillerParamsSet()
   nsRefPtr<OomVictimLogger> oomLogger = new OomVictimLogger();
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->AddObserver(oomLogger, "ipc:content-shutdown", false);
   }
 }
 
 void
-SetProcessPriority(int aPid,
-                   ProcessPriority aPriority,
-                   uint32_t aBackgroundLRU)
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
 {
   HAL_LOG("SetProcessPriority(pid=%d, priority=%d, LRU=%u)",
-          aPid, aPriority, aBackgroundLRU);
+          aPid, aPriority, aLRU);
 
   // If this is the first time SetProcessPriority was called, set the kernel's
   // OOM parameters according to our prefs.
   //
   // We could/should do this on startup instead of waiting for the first
   // SetProcessPriorityCall.  But in practice, the master process needs to set
   // its priority early in the game, so we can reasonably rely on
   // SetProcessPriority being called early in startup.
   EnsureKernelLowMemKillerParamsSet();
 
   PriorityClass* pc = GetPriorityClass(aPriority);
 
   int oomScoreAdj = pc->OomScoreAdj();
 
-  RoundOomScoreAdjUpWithBackroundLRU(oomScoreAdj, aBackgroundLRU);
+  RoundOomScoreAdjUpWithLRU(oomScoreAdj, aLRU);
 
   // We try the newer interface first, and fall back to the older interface
   // on failure.
   if (!WriteToFile(nsPrintfCString("/proc/%d/oom_score_adj", aPid).get(),
                    nsPrintfCString("%d", oomScoreAdj).get()))
   {
     WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
                 nsPrintfCString("%d", OomAdjOfOomScoreAdj(oomScoreAdj)).get());
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -350,19 +350,17 @@ DisableAlarm()
 bool
 SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
 {
   NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts.  Yet.");
   return false;
 }
 
 void
-SetProcessPriority(int aPid,
-                   ProcessPriority aPriority,
-                   uint32_t aBackgroundLRU)
+SetProcessPriority(int aPid, ProcessPriority aPriority, uint32_t aLRU)
 {
   NS_RUNTIMEABORT("Only the main process may set processes' priorities.");
 }
 
 void
 SetCurrentThreadPriority(ThreadPriority aThreadPriority)
 {
   NS_RUNTIMEABORT("Setting thread priority cannot be called from sandboxed contexts.");