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 101488 0e213ba77dca4032ddc876d788d83fa28eb48f49
parent 101487 05848864b5f9f908b60d4bac8922291119c70091
child 101489 f89ae41eed63ecc2ba51b7a2b5c2e29819854da3
push id13008
push userjlebar@mozilla.com
push dateSun, 05 Aug 2012 05:09:52 +0000
treeherdermozilla-inbound@0e213ba77dca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, cjones
bugs768832
milestone17.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 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.