Bug 768832 - Set OOM adjust for background windows. r=bz,cjones
authorJustin Lebar <justin.lebar@gmail.com>
Sun, 05 Aug 2012 01:09:39 -0400
changeset 104605 0e213ba77dca4032ddc876d788d83fa28eb48f49
parent 104604 05848864b5f9f908b60d4bac8922291119c70091
child 104606 f89ae41eed63ecc2ba51b7a2b5c2e29819854da3
push id1989
push userakeybl@mozilla.com
push dateTue, 28 Aug 2012 00:20:43 +0000
treeherdermozilla-aurora@a8e95ae10ea7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, cjones
bugs768832
milestone17.0a1
Bug 768832 - Set OOM adjust for background windows. r=bz,cjones
b2g/app/b2g.js
dom/ipc/Makefile.in
dom/ipc/ProcessPriorityManager.cpp
dom/ipc/ProcessPriorityManager.h
hal/Hal.cpp
hal/Hal.h
hal/HalTypes.h
hal/Makefile.in
hal/fallback/FallbackProcessPriority.cpp
hal/gonk/GonkHal.cpp
hal/sandbox/PHal.ipdl
hal/sandbox/SandboxHal.cpp
layout/build/nsLayoutStatics.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -478,8 +478,20 @@ pref("gfx.gralloc.enabled", false);
 // XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging 
 pref("javascript.options.mem.log", true);
 
 // Increase mark slice time from 10ms to 30ms
 pref("javascript.options.mem.gc_incremental_slice_ms", 30);
 
 // Show/Hide scrollbars when active/inactive
 pref("ui.showHideScrollbars", 1);
+
+// Enable the ProcessPriorityManager, and give processes with no visible
+// documents a 1s grace period before they're eligible to be marked as
+// background.
+pref("dom.ipc.processPriorityManager.enabled", true);
+pref("dom.ipc.processPriorityManager.gracePeriodMS", 1000);
+pref("hal.processPriorityManager.gonk.masterOomAdjust", 0);
+pref("hal.processPriorityManager.gonk.foregroundOomAdjust", 1);
+pref("hal.processPriorityManager.gonk.backgroundOomAdjust", 2);
+pref("hal.processPriorityManager.gonk.masterNice", -1);
+pref("hal.processPriorityManager.gonk.foregroundNice", 0);
+pref("hal.processPriorityManager.gonk.backgroundNice", 10);
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -35,26 +35,28 @@ EXPORTS_mozilla/dom = \
   StructuredCloneUtils.h \
   TabParent.h \
   TabChild.h \
   TabMessageUtils.h \
   $(NULL)
 
 EXPORTS_mozilla/dom/ipc = \
   Blob.h \
+  ProcessPriorityManager.h \
   nsIRemoteBlob.h \
   $(NULL)
 
 CPPSRCS = \
   Blob.cpp \
   ContentProcess.cpp \
   ContentParent.cpp \
   ContentChild.cpp \
   CrashReporterParent.cpp \
   CrashReporterChild.cpp \
+  ProcessPriorityManager.cpp \
   StructuredCloneUtils.cpp \
   TabParent.cpp \
   TabChild.cpp \
   TabMessageUtils.cpp \
   $(NULL)
 
 ifdef MOZ_SYDNEYAUDIO
 EXPORTS_mozilla/dom += \
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ipc/ProcessPriorityManager.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/TimeStamp.h"
+#include "prlog.h"
+#include "nsWeakPtr.h"
+#include "nsXULAppAPI.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsITimer.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIDocument.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMDocument.h"
+#include "nsPIDOMWindow.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+namespace {
+static bool sInitialized = false;
+
+// Some header defines a LOG macro, but we don't want it here.
+#ifdef LOG
+#undef LOG
+#endif
+
+// Enable logging by setting
+//
+//   NSPR_LOG_MODULES=ProcessPriorityManager:5
+//
+// in your environment.
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* logModule = PR_NewLogModule("ProcessPriorityManager");
+#define LOG(fmt, ...) \
+  PR_LOG(logModule, PR_LOG_DEBUG, \
+         ("[%d] ProcessPriorityManager - " fmt, getpid(), ##__VA_ARGS__))
+#else
+#define LOG(fmt, ...)
+#endif
+
+/**
+ * This class listens to window creation and visibilitychange events and
+ * informs the hal back-end when this process transitions between having no
+ * visible top-level windows, and when it has at least one visible top-level
+ * window.
+ *
+ *
+ * An important heuristic here is that we don't mark a process as background
+ * until it's had no visible top-level windows for some amount of time.
+ *
+ * We do this because the notion of visibility is tied to inner windows
+ * (actually, to documents).  When we navigate a page with outer window W, we
+ * first destroy W's inner window and document, then insert a new inner window
+ * and document into W.  If not for our grace period, this transition could
+ * cause us to inform hal that this process quickly transitioned from
+ * foreground to background to foreground again.
+ *
+ */
+class ProcessPriorityManager MOZ_FINAL
+  : public nsIObserver
+  , public nsIDOMEventListener
+{
+public:
+  ProcessPriorityManager();
+  void Init();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIDOMEVENTLISTENER
+
+private:
+  void SetPriority(ProcessPriority aPriority);
+  void OnContentDocumentGlobalCreated(nsISupports* aOuterWindow);
+  void OnInnerWindowDestroyed();
+  void OnGracePeriodTimerFired();
+  void RecomputeNumVisibleWindows();
+
+  // mProcessPriority tracks the priority we've given this process in hal,
+  // except that, when the grace period timer is active,
+  // mProcessPriority == BACKGROUND even though hal still thinks we're a
+  // foreground process.
+  ProcessPriority mProcessPriority;
+
+  nsTArray<nsWeakPtr> mWindows;
+  nsCOMPtr<nsITimer> mGracePeriodTimer;
+  TimeStamp mStartupTime;
+};
+
+NS_IMPL_ISUPPORTS2(ProcessPriorityManager, nsIObserver, nsIDOMEventListener);
+
+ProcessPriorityManager::ProcessPriorityManager()
+  : mProcessPriority(PROCESS_PRIORITY_FOREGROUND)
+  , mStartupTime(TimeStamp::Now())
+{
+}
+
+void
+ProcessPriorityManager::Init()
+{
+  LOG("Starting up.");
+
+  // We can't do this in the constructor because we need to hold a strong ref
+  // to |this| before calling these methods.
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  os->AddObserver(this, "content-document-global-created", /* ownsWeak = */ false);
+  os->AddObserver(this, "inner-window-destroyed", /* ownsWeak = */ false);
+
+  SetPriority(PROCESS_PRIORITY_FOREGROUND);
+}
+
+NS_IMETHODIMP
+ProcessPriorityManager::Observe(
+  nsISupports* aSubject,
+  const char* aTopic,
+  const PRUnichar* aData)
+{
+  if (!strcmp(aTopic, "content-document-global-created")) {
+    OnContentDocumentGlobalCreated(aSubject);
+  } else if (!strcmp(aTopic, "inner-window-destroyed")) {
+    OnInnerWindowDestroyed();
+  } else if (!strcmp(aTopic, "timer-callback")) {
+    OnGracePeriodTimerFired();
+  } else {
+    MOZ_ASSERT(false);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ProcessPriorityManager::HandleEvent(
+  nsIDOMEvent* aEvent)
+{
+  LOG("Got visibilitychange.");
+  RecomputeNumVisibleWindows();
+  return NS_OK;
+}
+
+void
+ProcessPriorityManager::OnContentDocumentGlobalCreated(
+  nsISupports* aOuterWindow)
+{
+  // Get the inner window (the topic of content-document-global-created is
+  // the /outer/ window!).
+  nsCOMPtr<nsPIDOMWindow> outerWindow = do_QueryInterface(aOuterWindow);
+  NS_ENSURE_TRUE(outerWindow, );
+  nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow->GetCurrentInnerWindow();
+  NS_ENSURE_TRUE(innerWindow, );
+
+  // We're only interested in top-level windows.
+  nsCOMPtr<nsIDOMWindow> parentOuterWindow;
+  innerWindow->GetScriptableParent(getter_AddRefs(parentOuterWindow));
+  NS_ENSURE_TRUE(parentOuterWindow, );
+  if (parentOuterWindow != outerWindow) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(innerWindow);
+  NS_ENSURE_TRUE(target, );
+
+  nsWeakPtr weakWin = do_GetWeakReference(innerWindow);
+  NS_ENSURE_TRUE(weakWin, );
+
+  if (mWindows.Contains(weakWin)) {
+    return;
+  }
+
+  target->AddSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"),
+                                 this,
+                                 /* useCapture = */ false,
+                                 /* wantsUntrusted = */ false);
+
+  mWindows.AppendElement(weakWin);
+  RecomputeNumVisibleWindows();
+}
+
+void
+ProcessPriorityManager::OnInnerWindowDestroyed()
+{
+  RecomputeNumVisibleWindows();
+}
+
+void
+ProcessPriorityManager::RecomputeNumVisibleWindows()
+{
+  // We could try to be clever and count the number of visible windows, instead
+  // of iterating over mWindows every time one window's visibility state changes.
+  // But experience suggests that iterating over the windows is prone to fewer
+  // errors (and one mistake doesn't mess you up for the entire session).
+  // Moreover, mWindows should be a very short list, since it contains only
+  // top-level content windows.
+
+  bool allHidden = true;
+  for (PRUint32 i = 0; i < mWindows.Length(); i++) {
+    nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mWindows[i]);
+    if (!window) {
+      mWindows.RemoveElementAt(i);
+      i--;
+      continue;
+    }
+
+    nsCOMPtr<nsIDOMDocument> doc;
+    window->GetDocument(getter_AddRefs(doc));
+    if (!doc) {
+      continue;
+    }
+
+    bool hidden = false;
+    doc->GetMozHidden(&hidden);
+#ifdef DEBUG
+    nsAutoString spec;
+    doc->GetDocumentURI(spec);
+    LOG("Document at %s has visibility %d.", NS_ConvertUTF16toUTF8(spec).get(), !hidden);
+#endif
+
+    allHidden = allHidden && hidden;
+
+    // We could break out early from this loop if
+    //   !hidden && mProcessPriority == BACKGROUND,
+    // but then we might not clean up all the weak refs.
+  }
+
+  SetPriority(allHidden ?
+              PROCESS_PRIORITY_BACKGROUND :
+              PROCESS_PRIORITY_FOREGROUND);
+}
+
+void
+ProcessPriorityManager::SetPriority(ProcessPriority aPriority)
+{
+  if (aPriority == mProcessPriority) {
+    return;
+  }
+
+  if (aPriority == PROCESS_PRIORITY_BACKGROUND) {
+    // If this is a foreground --> background transition, give ourselves a
+    // grace period before informing hal.
+    PRUint32 gracePeriodMS = Preferences::GetUint("dom.ipc.processPriorityManager.gracePeriodMS", 1000);
+    if (mGracePeriodTimer) {
+      LOG("Grace period timer already active.");
+      return;
+    }
+
+    LOG("Initializing grace period timer.");
+    mProcessPriority = aPriority;
+    mGracePeriodTimer = do_CreateInstance("@mozilla.org/timer;1");
+    mGracePeriodTimer->Init(this, gracePeriodMS, nsITimer::TYPE_ONE_SHOT);
+
+  } else if (aPriority == PROCESS_PRIORITY_FOREGROUND) {
+    // If this is a background --> foreground transition, do it immediately, and
+    // cancel the outstanding grace period timer, if there is one.
+    if (mGracePeriodTimer) {
+      mGracePeriodTimer->Cancel();
+      mGracePeriodTimer = nullptr;
+    }
+
+    LOG("Setting priority to %d.", aPriority);
+    mProcessPriority = aPriority;
+    hal::SetProcessPriority(getpid(), aPriority);
+
+  } else {
+    MOZ_ASSERT(false);
+  }
+}
+
+void
+ProcessPriorityManager::OnGracePeriodTimerFired()
+{
+  LOG("Grace period timer fired; setting priority to %d.",
+      PROCESS_PRIORITY_BACKGROUND);
+
+  // mProcessPriority should already be BACKGROUND: We set it in
+  // SetPriority(BACKGROUND), and we canceled this timer if there was an
+  // intervening SetPriority(FOREGROUND) call.
+  MOZ_ASSERT(mProcessPriority == PROCESS_PRIORITY_BACKGROUND);
+
+  mGracePeriodTimer = nullptr;
+  hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_BACKGROUND);
+}
+
+} // anonymous namespace
+
+void
+InitProcessPriorityManager()
+{
+  if (sInitialized) {
+    return;
+  }
+
+  // If IPC tabs aren't enabled at startup, don't bother with any of this.
+  if (!Preferences::GetBool("dom.ipc.processPriorityManager.enabled") ||
+      Preferences::GetBool("dom.ipc.tabs.disabled")) {
+    return;
+  }
+
+  sInitialized = true;
+
+  // If we're the master process, mark ourselves as such and don't create a
+  // ProcessPriorityManager (we never want to mark the master process as
+  // backgrounded).
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    LOG("This is the master process.");
+    hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER);
+    return;
+  }
+
+  // This object is held alive by the observer service.
+  nsRefPtr<ProcessPriorityManager> mgr = new ProcessPriorityManager();
+  mgr->Init();
+}
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ProcessPriorityManager_h_
+#define mozilla_ProcessPriorityManager_h_
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+/**
+ * Initialize the ProcessPriorityManager.
+ *
+ * The ProcessPriorityManager informs the hal back-end whether this is the root
+ * Gecko process, and, if we're not the root, informs hal when this process
+ * transitions between having no visible top-level windows, and having at least
+ * one visible top-level window.
+ *
+ * Hal may adjust this process's operating system priority (e.g. niceness, on
+ * *nix) according to these notificaitons.
+ *
+ * This function call does nothing if the pref for OOP tabs is not set.
+ */
+void InitProcessPriorityManager();
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -17,16 +17,21 @@
 #include "nsIWebNavigation.h"
 #include "nsITabChild.h"
 #include "nsIDocShell.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "WindowIdentifier.h"
 #include "mozilla/dom/ScreenOrientation.h"
 
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#endif
+
 using namespace mozilla::services;
 
 #define PROXY_IF_SANDBOXED(_call)                 \
   do {                                            \
     if (InSandbox()) {                            \
       hal_sandbox::_call;                         \
     } else {                                      \
       hal_impl::_call;                            \
@@ -723,10 +728,21 @@ NotifyAlarmFired()
 bool
 SetAlarm(PRInt32 aSeconds, PRInt32 aNanoseconds)
 {
   // It's pointless to program an alarm nothing is going to observe ...
   MOZ_ASSERT(sAlarmObserver);
   RETURN_PROXY_IF_SANDBOXED(SetAlarm(aSeconds, aNanoseconds));
 }
 
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority)
+{
+  if (InSandbox()) {
+    hal_sandbox::SetProcessPriority(aPid, aPriority);
+  }
+  else {
+    hal_impl::SetProcessPriority(aPid, aPriority);
+  }
+}
+
 } // namespace hal
 } // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -397,16 +397,25 @@ void NotifyAlarmFired();
  *
  * The alarm can be reprogrammed at any time.
  *
  * This API is currently only allowed to be used from non-sandboxed
  * contexts.
  */
 bool SetAlarm(PRInt32 aSeconds, PRInt32 aNanoseconds);
 
+/**
+ * Set the priority of the given process.
+ *
+ * Exactly what this does will vary between platforms.  On *nix we might give
+ * background processes higher nice values.  On other platforms, we might
+ * ignore this call entirely.
+ */
+void SetProcessPriority(int aPid, hal::ProcessPriority aPriority);
+
 } // namespace MOZ_HAL_NAMESPACE
 } // namespace mozilla
 
 #ifdef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_HAL_NAMESPACE
 #endif
 
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -49,33 +49,35 @@ enum SwitchDevice {
 enum SwitchState {
   SWITCH_STATE_UNKNOWN = -1,
   SWITCH_STATE_ON,
   SWITCH_STATE_OFF,
   NUM_SWITCH_STATE
 };
 
 typedef Observer<SwitchEvent> SwitchObserver;
-} // namespace hal
-} // namespace mozilla
 
-namespace mozilla {
-namespace hal {
+enum ProcessPriority {
+  PROCESS_PRIORITY_BACKGROUND,
+  PROCESS_PRIORITY_FOREGROUND,
+  PROCESS_PRIORITY_MASTER,
+  NUM_PROCESS_PRIORITY
+};
 
 /**
  * Used by ModifyWakeLock
  */
 enum WakeLockControl {
   WAKE_LOCK_REMOVE_ONE = -1,
   WAKE_LOCK_NO_CHANGE  = 0,
   WAKE_LOCK_ADD_ONE    = 1,
 };
 
-}
-}
+} // namespace hal
+} // namespace mozilla
 
 namespace IPC {
 
 /**
  * Light type serializer.
  */
 template <>
 struct ParamTraits<mozilla::hal::LightType>
@@ -129,12 +131,19 @@ struct ParamTraits<mozilla::hal::SwitchS
  */
 template <>
 struct ParamTraits<mozilla::hal::SwitchDevice>:
   public EnumSerializer<mozilla::hal::SwitchDevice,
                         mozilla::hal::SWITCH_DEVICE_UNKNOWN,
                         mozilla::hal::NUM_SWITCH_DEVICE> {
 };
 
+template <>
+struct ParamTraits<mozilla::hal::ProcessPriority>:
+  public EnumSerializer<mozilla::hal::ProcessPriority,
+                        mozilla::hal::PROCESS_PRIORITY_BACKGROUND,
+                        mozilla::hal::NUM_PROCESS_PRIORITY> {
+};
+
 
 } // namespace IPC
 
 #endif // mozilla_hal_Types_h
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -119,16 +119,17 @@ endif
 # Fallbacks for backends implemented on Gonk only.
 ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) #{
 CPPSRCS += \
   FallbackLights.cpp  \
   FallbackTime.cpp \
   FallbackWakeLocks.cpp \
   FallbackSwitch.cpp \
   FallbackScreenPower.cpp \
+  FallbackProcessPriority.cpp \
   $(NULL)
 endif #}
 
 # Fallbacks for backends implemented on Android only.
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += FallbackNetwork.cpp
 endif
 
new file mode 100644
--- /dev/null
+++ b/hal/fallback/FallbackProcessPriority.cpp
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority)
+{
+  HAL_LOG(("FallbackProcessPriority - SetProcessPriority(%d, %d)\n", aPid, aPriority));
+}
+
+} // hal_impl
+} // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -16,16 +16,17 @@
  */
 
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/android_alarm.h>
 #include <math.h>
 #include <stdio.h>
 #include <sys/syscall.h>
+#include <sys/resource.h>
 #include <time.h>
 
 #include "android/log.h"
 #include "cutils/properties.h"
 #include "hardware/hardware.h"
 #include "hardware/lights.h"
 #include "hardware_legacy/uevent.h"
 #include "hardware_legacy/vibrator.h"
@@ -34,34 +35,37 @@
 #include "base/message_loop.h"
 
 #include "Hal.h"
 #include "HalImpl.h"
 #include "mozilla/dom/battery/Constants.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Services.h"
+#include "mozilla/Preferences.h"
 #include "nsAlgorithm.h"
+#include "nsPrintfCString.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIRunnable.h"
 #include "nsScreenManagerGonk.h"
 #include "nsThreadUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIThread.h"
 #include "nsXULAppAPI.h"
 #include "OrientationObserver.h"
 #include "UeventPoller.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
 #define NsecPerMsec  1000000
 #define NsecPerSec   1000000000
 
 
-using mozilla::hal::WindowIdentifier;
+using namespace mozilla;
+using namespace mozilla::hal;
 
 namespace mozilla {
 namespace hal_impl {
 
 namespace {
 
 /**
  * This runnable runs for the lifetime of the program, once started.  It's
@@ -795,10 +799,55 @@ SetAlarm(PRInt32 aSeconds, PRInt32 aNano
   if (result < 0) {
     HAL_LOG(("Unable to set alarm: %s.", strerror(errno)));
     return false;
   }
 
   return true;
 }
 
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority)
+{
+  HAL_LOG(("SetProcessPriority(pid=%d, priority=%d)", aPid, aPriority));
+
+  const char* priorityStr = NULL;
+  switch (aPriority) {
+  case PROCESS_PRIORITY_BACKGROUND:
+    priorityStr = "background";
+    break;
+  case PROCESS_PRIORITY_FOREGROUND:
+    priorityStr = "foreground";
+    break;
+  case PROCESS_PRIORITY_MASTER:
+    priorityStr = "master";
+    break;
+  default:
+    MOZ_NOT_REACHED();
+  }
+
+  // Notice that you can disable oom_adj and renice by deleting the prefs
+  // hal.processPriorityManager{foreground,background,master}{OomAdjust,Nice}.
+
+  PRInt32 oomAdj = 0;
+  nsresult rv = Preferences::GetInt(nsPrintfCString(
+    "hal.processPriorityManager.gonk.%sOomAdjust", priorityStr).get(), &oomAdj);
+  if (NS_SUCCEEDED(rv)) {
+    HAL_LOG(("Setting oom_adj for pid %d to %d", aPid, oomAdj));
+    WriteToFile(nsPrintfCString("/proc/%d/oom_adj", aPid).get(),
+                nsPrintfCString("%d", oomAdj).get());
+  }
+
+  PRInt32 nice = 0;
+  rv = Preferences::GetInt(nsPrintfCString(
+    "hal.processPriorityManager.gonk.%sNice", priorityStr).get(), &nice);
+  if (NS_SUCCEEDED(rv)) {
+    HAL_LOG(("Setting nice for pid %d to %d", aPid, nice));
+
+    int success = setpriority(PRIO_PROCESS, aPid, nice);
+    if (success != 0) {
+      HAL_LOG(("Failed to set nice for pid %d to %d", aPid, nice));
+    }
+  }
+}
+
 } // hal_impl
 } // mozilla
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -16,16 +16,17 @@ using mozilla::dom::ScreenOrientation;
 using mozilla::hal::FlashMode;
 using mozilla::hal::LightType;
 using mozilla::hal::LightMode;
 using mozilla::hal::SensorType;
 using mozilla::hal::SensorAccuracyType;
 using mozilla::hal::WakeLockControl;
 using mozilla::hal::SwitchState;
 using mozilla::hal::SwitchDevice;
+using mozilla::hal::ProcessPriority;
 using nsIntRect;
 using PRTime;
 
 namespace mozilla {
 
 namespace hal {
 struct BatteryInformation {
   double level;
@@ -66,17 +67,18 @@ struct WakeLockInformation {
 };
 
 struct ScreenConfiguration {
   nsIntRect rect;
   ScreenOrientation orientation;
   uint32_t colorDepth;
   uint32_t pixelDepth;
 };
-}
+
+} // namespace hal
 
 namespace hal_sandbox {
 
 sync protocol PHal {
     manager PContent;
 
 child:
     NotifyBatteryChange(BatteryInformation aBatteryInfo);
@@ -133,16 +135,18 @@ parent:
       returns (bool allowed);
     UnlockScreenOrientation();
  
     EnableSwitchNotifications(SwitchDevice aDevice);
     DisableSwitchNotifications(SwitchDevice aDevice);
     sync GetCurrentSwitchState(SwitchDevice aDevice)
       returns (SwitchState aState);
 
+    SetProcessPriority(int aPid, ProcessPriority aPriority);
+
 child:
     NotifySensorChange(SensorData aSensorData);
 
 parent:    
     EnableSensorNotifications(SensorType aSensor);
     DisableSensorNotifications(SensorType aSensor);
 
     __delete__();
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -275,16 +275,22 @@ DisableAlarm()
 
 bool
 SetAlarm(PRInt32 aSeconds, PRInt32 aNanoseconds)
 {
   NS_RUNTIMEABORT("Alarms can't be programmed from sandboxed contexts.  Yet.");
   return false;
 }
 
+void
+SetProcessPriority(int aPid, ProcessPriority aPriority)
+{
+  Hal()->SendSetProcessPriority(aPid, aPriority);
+}
+
 class HalParent : public PHalParent
                 , public BatteryObserver
                 , public NetworkObserver
                 , public ISensorObserver
                 , public WakeLockObserver
                 , public ScreenConfigurationObserver
                 , public SwitchObserver
 {
@@ -563,16 +569,25 @@ public:
   }
 
   virtual bool
   RecvGetCurrentSwitchState(const SwitchDevice& aDevice, hal::SwitchState *aState) MOZ_OVERRIDE
   {
     *aState = hal::GetCurrentSwitchState(aDevice);
     return true;
   }
+
+  virtual bool
+  RecvSetProcessPriority(const int& aPid, const ProcessPriority& aPriority)
+  {
+    // TODO As a security check, we should ensure that aPid is either the pid
+    // of our child, or the pid of one of the child's children.
+    hal::SetProcessPriority(aPid, aPriority);
+    return true;
+  }
 };
 
 class HalChild : public PHalChild {
 public:
   virtual bool
   RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) MOZ_OVERRIDE {
     hal::NotifyBatteryChange(aBatteryInfo);
     return true;
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -89,21 +89,23 @@
 #include "nsJSEnvironment.h"
 #include "nsContentSink.h"
 #include "nsFrameMessageManager.h"
 #include "nsRefreshDriver.h"
 #include "nsDOMMutationObserver.h"
 #include "nsHyphenationManager.h"
 #include "nsEditorSpellCheck.h"
 #include "nsWindowMemoryReporter.h"
+#include "mozilla/dom/ipc/ProcessPriorityManager.h"
 
 extern void NS_ShutdownChainItemPool();
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
 
 nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
 
 nsresult
 nsLayoutStatics::Initialize()
 {
   NS_ASSERTION(sLayoutStaticRefcnt == 0,
                "nsLayoutStatics isn't zero!");
@@ -238,16 +240,18 @@ nsLayoutStatics::Initialize()
   nsFrameList::Init();
 
   NS_SealStaticAtomTable();
 
   nsWindowMemoryReporter::Init();
 
   nsSVGUtils::Init();
 
+  InitProcessPriorityManager();
+
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
   // Don't need to shutdown nsWindowMemoryReporter, that will be done by the
   // memory reporter manager.