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 262209 908eaacff6f499831472cfb923d91afd8b2e3d0b
parent 262208 688fdf0758a281ffabd633c94f0b0c256a65ca91
child 262210 a30c985bc8dd336683b1b65d672b08577909f89d
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands, khuey
bugs892371
milestone39.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 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.");