Bug 980027: Part 1: Provide mechanism to set thread priority via HAL. r=gsvelto
authorBen Kelly <ben@wanderview.com>
Mon, 17 Mar 2014 11:52:42 -0400
changeset 173918 098a43b537e927416d1a0aee067dd04a5a3bd3f6
parent 173917 b990c93d098de9ebdc8dadb2281b676fceeb11c9
child 173919 6344d660651730f541359cbad751de00a97853e8
push id5746
push userryanvm@gmail.com
push dateMon, 17 Mar 2014 21:34:38 +0000
treeherderfx-team@2dda6b95610e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgsvelto
bugs980027
milestone30.0a1
Bug 980027: Part 1: Provide mechanism to set thread priority via HAL. r=gsvelto
hal/Hal.cpp
hal/Hal.h
hal/HalTypes.h
hal/fallback/FallbackThreadPriority.cpp
hal/gonk/GonkHal.cpp
hal/moz.build
hal/sandbox/SandboxHal.cpp
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -866,16 +866,22 @@ SetProcessPriority(int aPid,
 {
   // 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, aCPUPriority,
                                         aBackgroundLRU));
 }
 
+void
+SetCurrentThreadPriority(ThreadPriority aPriority)
+{
+  PROXY_IF_SANDBOXED(SetCurrentThreadPriority(aPriority));
+}
+
 // From HalTypes.h.
 const char*
 ProcessPriorityToString(ProcessPriority aPriority)
 {
   switch (aPriority) {
   case PROCESS_PRIORITY_MASTER:
     return "MASTER";
   case PROCESS_PRIORITY_FOREGROUND_HIGH:
@@ -893,16 +899,28 @@ ProcessPriorityToString(ProcessPriority 
   case PROCESS_PRIORITY_UNKNOWN:
     return "UNKNOWN";
   default:
     MOZ_ASSERT(false);
     return "???";
   }
 }
 
+const char *
+ThreadPriorityToString(ThreadPriority aPriority)
+{
+  switch (aPriority) {
+  case THREAD_PRIORITY_COMPOSITOR:
+    return "COMPOSITOR";
+  default:
+    MOZ_ASSERT(false);
+    return "???";
+  }
+}
+
 // From HalTypes.h.
 const char*
 ProcessPriorityToString(ProcessPriority aPriority,
                         ProcessCPUPriority aCPUPriority)
 {
   // Sorry this is ugly.  At least it's all in one place.
   //
   // We intentionally fall through if aCPUPriority is invalid; we won't hit any
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -492,16 +492,23 @@ bool SetAlarm(int32_t aSeconds, int32_t 
  * ignore this call entirely.
  */
 void SetProcessPriority(int aPid,
                         hal::ProcessPriority aPriority,
                         hal::ProcessCPUPriority aCPUPriority,
                         uint32_t aLRU = 0);
 
 /**
+ * Set the current thread's priority to appropriate platform-specific value for
+ * given functionality.  Instead of providing arbitrary priority numbers you
+ * must specify a type of function like THREAD_PRIORITY_COMPOSITOR.
+ */
+void SetCurrentThreadPriority(hal::ThreadPriority aPriority);
+
+/**
  * Register an observer for the FM radio.
  */
 void RegisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
 
 /**
  * Unregister the observer for the FM radio.
  */
 void UnregisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -94,29 +94,47 @@ enum ProcessPriority {
 };
 
 enum ProcessCPUPriority {
   PROCESS_CPU_PRIORITY_LOW,
   PROCESS_CPU_PRIORITY_NORMAL,
   NUM_PROCESS_CPU_PRIORITY
 };
 
+// Values that can be passed to hal::SetThreadPriority().  These should be
+// functional in nature, such as COMPOSITOR, instead of levels, like LOW/HIGH.
+// This allows us to tune our priority scheme for the system in one place such
+// that it makes sense holistically for the overall operating system.  On gonk
+// or android we may want different priority schemes than on windows, etc.
+enum ThreadPriority {
+  THREAD_PRIORITY_COMPOSITOR,
+  NUM_THREAD_PRIORITY
+};
+
 // Convert a ProcessPriority enum value (with an optional ProcessCPUPriority)
 // to a string.  The strings returned by this function are statically
 // allocated; do not attempt to free one!
 //
 // If you pass an unknown process priority (or NUM_PROCESS_PRIORITY), we
 // fatally assert in debug builds and otherwise return "???".
 const char*
 ProcessPriorityToString(ProcessPriority aPriority);
 
 const char*
 ProcessPriorityToString(ProcessPriority aPriority,
                         ProcessCPUPriority aCPUPriority);
 
+// Convert a ThreadPriority enum value to a string.  The strings returned by
+// this function are statically allocated; do not attempt to free one!
+//
+// If you pass an unknown process priority (or NUM_THREAD_PRIORITY), we
+// fatally assert in debug builds and otherwise return "???".
+const char *
+ThreadPriorityToString(ThreadPriority aPriority);
+
 /**
  * Used by ModifyWakeLock
  */
 enum WakeLockControl {
   WAKE_LOCK_REMOVE_ONE = -1,
   WAKE_LOCK_NO_CHANGE  = 0,
   WAKE_LOCK_ADD_ONE    = 1,
   NUM_WAKE_LOCK
new file mode 100644
--- /dev/null
+++ b/hal/fallback/FallbackThreadPriority.cpp
@@ -0,0 +1,20 @@
+/* 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
+SetCurrentThreadPriority(ThreadPriority aPriority)
+{
+  HAL_LOG(("FallbackThreadPriority - SetCurrentThreadPriority(%d)\n",
+           ThreadPriorityToString(aPriority)));
+}
+
+} // hal_impl
+} // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -22,27 +22,29 @@
 #include <math.h>
 #include <regex.h>
 #include <stdio.h>
 #include <sys/klog.h>
 #include <sys/syscall.h>
 #include <sys/resource.h>
 #include <time.h>
 #include <asm/page.h>
+#include <sched.h>
 
 #include "mozilla/DebugOnly.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"
 #include "hardware_legacy/power.h"
 #include "libdisplay/GonkDisplay.h"
+#include "utils/threads.h"
 
 #include "base/message_loop.h"
 
 #include "Hal.h"
 #include "HalImpl.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/battery/Constants.h"
 #include "mozilla/FileUtils.h"
@@ -1343,28 +1345,45 @@ SetNiceForPid(int aPid, int aNice)
       // (The |tidlong == aPid| check is very important; without it, we'll
       // renice aPid twice, and the second renice will be relative to the
       // priority set by the first renice.)
       continue;
     }
 
     int tid = static_cast<int>(tidlong);
 
+    // Do not set the priority of threads running with a real-time policy
+    // as part of the bulk process adjustment.  These threads need to run
+    // at their specified priority in order to meet timing guarantees.
+    int schedPolicy = sched_getscheduler(tid);
+    if (schedPolicy == SCHED_FIFO || schedPolicy == SCHED_RR) {
+      continue;
+    }
+
     errno = 0;
     // Get and set the task's new priority.
     int origtaskpriority = getpriority(PRIO_PROCESS, tid);
     if (errno) {
       LOG("Unable to get nice for tid=%d (pid=%d); error %d.  This isn't "
           "necessarily a problem; it could be a benign race condition.",
           tid, aPid, errno);
       continue;
     }
 
     int newtaskpriority =
       std::max(origtaskpriority - origProcPriority + aNice, aNice);
+
+    // Do not reduce priority of threads already running at priorities greater
+    // than normal.  These threads are likely special service threads that need
+    // elevated priorities to process audio, display composition, etc.
+    if (newtaskpriority > origtaskpriority &&
+        origtaskpriority < ANDROID_PRIORITY_NORMAL) {
+      continue;
+    }
+
     rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
 
     if (rv) {
       LOG("Unable to set nice for tid=%d (pid=%d); error %d.  This isn't "
           "necessarily a problem; it could be a benign race condition.",
           tid, aPid, errno);
       continue;
     }
@@ -1450,16 +1469,64 @@ SetProcessPriority(int aPid,
 
   if (NS_SUCCEEDED(rv)) {
     LOG("Setting nice for pid %d to %d", aPid, nice);
     SetNiceForPid(aPid, nice);
   }
 }
 
 void
+SetCurrentThreadPriority(ThreadPriority aPriority)
+{
+  int policy = SCHED_OTHER;
+  int priorityOrNice = ANDROID_PRIORITY_NORMAL;
+
+  switch(aPriority) {
+  case THREAD_PRIORITY_COMPOSITOR:
+    priorityOrNice = Preferences::GetInt("hal.gonk.compositor.rt_priority", 0);
+    if (priorityOrNice >= sched_get_priority_min(SCHED_FIFO) &&
+        priorityOrNice <= sched_get_priority_max(SCHED_FIFO)) {
+      policy = SCHED_FIFO;
+    } else {
+      priorityOrNice = Preferences::GetInt("hal.gonk.compositor.nice",
+                                           ANDROID_PRIORITY_URGENT_DISPLAY);
+    }
+    break;
+  default:
+    LOG("Unrecognized thread priority %d; Doing nothing", aPriority);
+    return;
+  }
+
+  int tid = gettid();
+  int rv = 0;
+
+  // If a RT scheduler policy is used, then we must set the priority using
+  // sched_setscheduler() and the sched_param.sched_priority value.
+  if (policy == SCHED_FIFO || policy == SCHED_RR) {
+    LOG("Setting thread %d to priority level %s; RT priority %d",
+        tid, ThreadPriorityToString(aPriority), priorityOrNice);
+    sched_param schedParam;
+    schedParam.sched_priority = priorityOrNice;
+    rv = sched_setscheduler(tid, policy, &schedParam);
+
+  // Otherwise priority is solely defined by the nice level, so use the
+  // setpriority() function.
+  } else {
+    LOG("Setting thread %d to priority level %s; nice level %d",
+        tid, ThreadPriorityToString(aPriority), priorityOrNice);
+    rv = setpriority(PRIO_PROCESS, tid, priorityOrNice);
+  }
+
+  if (rv) {
+    LOG("Failed to set thread %d to priority level %s; error code %d",
+        tid, ThreadPriorityToString(aPriority), rv);
+  }
+}
+
+void
 FactoryReset()
 {
   nsCOMPtr<nsIRecoveryService> recoveryService =
     do_GetService("@mozilla.org/recovery-service;1");
   if (!recoveryService) {
     NS_WARNING("Could not get recovery service!");
     return;
   }
--- a/hal/moz.build
+++ b/hal/moz.build
@@ -146,16 +146,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk
     UNIFIED_SOURCES += [
         'fallback/FallbackDiskSpaceWatcher.cpp',
         'fallback/FallbackFactoryReset.cpp',
         'fallback/FallbackFMRadio.cpp',
         'fallback/FallbackLights.cpp',
         'fallback/FallbackProcessPriority.cpp',
         'fallback/FallbackScreenPower.cpp',
         'fallback/FallbackSwitch.cpp',
+        'fallback/FallbackThreadPriority.cpp',
         'fallback/FallbackTime.cpp',
         'fallback/FallbackWakeLocks.cpp',
     ]
 
 # Fallbacks for backends implemented on Android only.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     UNIFIED_SOURCES += [
         'fallback/FallbackNetwork.cpp',
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -358,16 +358,22 @@ SetProcessPriority(int aPid,
                    ProcessPriority aPriority,
                    ProcessCPUPriority aCPUPriority,
                    uint32_t aBackgroundLRU)
 {
   NS_RUNTIMEABORT("Only the main process may set processes' priorities.");
 }
 
 void
+SetCurrentThreadPriority(ThreadPriority aPriority)
+{
+  NS_RUNTIMEABORT("Only the main process may set thread priorities.");
+}
+
+void
 EnableFMRadio(const hal::FMRadioSettings& aSettings)
 {
   NS_RUNTIMEABORT("FM radio cannot be called from sandboxed contexts.");
 }
 
 void
 DisableFMRadio()
 {