From da2b2f344d653592b47e9836b1fd196755fe6a8e Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Mon, 5 May 2014 11:37:00 -0400 Subject: [PATCH] Bug 980027 - Part 1: Provide mechanism to set thread priorities. r=gsvelto, r=dhylands --- hal/Hal.cpp | 18 +++ hal/Hal.h | 8 ++ hal/HalTypes.h | 36 +++++- hal/fallback/FallbackThreadPriority.cpp | 20 ++++ hal/gonk/GonkHal.cpp | 147 ++++++++++++++++++++++++ hal/moz.build | 1 + hal/sandbox/SandboxHal.cpp | 6 + 7 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 hal/fallback/FallbackThreadPriority.cpp diff --git a/hal/Hal.cpp b/hal/Hal.cpp index e7de30971feb..58834564addb 100644 --- a/hal/Hal.cpp +++ b/hal/Hal.cpp @@ -871,6 +871,12 @@ SetProcessPriority(int aPid, aBackgroundLRU)); } +void +SetCurrentThreadPriority(hal::ThreadPriority aThreadPriority) +{ + PROXY_IF_SANDBOXED(SetCurrentThreadPriority(aThreadPriority)); +} + // From HalTypes.h. const char* ProcessPriorityToString(ProcessPriority aPriority) @@ -900,6 +906,18 @@ ProcessPriorityToString(ProcessPriority aPriority) } } +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, diff --git a/hal/Hal.h b/hal/Hal.h index 829deec6d847..0d35a7e2e48c 100644 --- a/hal/Hal.h +++ b/hal/Hal.h @@ -496,6 +496,14 @@ void SetProcessPriority(int aPid, 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 aThreadPriority); + /** * Register an observer for the FM radio. */ diff --git a/hal/HalTypes.h b/hal/HalTypes.h index f6a59d8e8004..12cfcad516c4 100644 --- a/hal/HalTypes.h +++ b/hal/HalTypes.h @@ -102,12 +102,26 @@ enum ProcessCPUPriority { NUM_PROCESS_CPU_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 "???". +/** + * Values that can be passed to hal::SetCurrentThreadPriority(). 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, we fatally assert in debug + * builds and otherwise return "???". + */ const char* ProcessPriorityToString(ProcessPriority aPriority); @@ -115,6 +129,16 @@ 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, we assert in debug builds + * and otherwise return "???". + */ +const char * +ThreadPriorityToString(ThreadPriority aPriority); + /** * Used by ModifyWakeLock */ diff --git a/hal/fallback/FallbackThreadPriority.cpp b/hal/fallback/FallbackThreadPriority.cpp new file mode 100644 index 000000000000..afab11a1889d --- /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 diff --git a/hal/gonk/GonkHal.cpp b/hal/gonk/GonkHal.cpp index 5bc56bc6621c..ae1e78d31492 100644 --- a/hal/gonk/GonkHal.cpp +++ b/hal/gonk/GonkHal.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include "hardware_legacy/vibrator.h" #include "hardware_legacy/power.h" #include "libdisplay/GonkDisplay.h" +#include "utils/threads.h" #include "base/message_loop.h" @@ -1357,6 +1359,14 @@ SetNiceForPid(int aPid, int aNice) int tid = static_cast(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); @@ -1369,6 +1379,15 @@ SetNiceForPid(int aPid, int aNice) 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) { @@ -1463,6 +1482,134 @@ SetProcessPriority(int aPid, } } +static bool +IsValidRealTimePriority(int aValue, int aSchedulePolicy) +{ + return (aValue >= sched_get_priority_min(aSchedulePolicy)) && + (aValue <= sched_get_priority_max(aSchedulePolicy)); +} + +static void +SetThreadNiceValue(pid_t aTid, ThreadPriority aThreadPriority, int aValue) +{ + MOZ_ASSERT(aThreadPriority < NUM_THREAD_PRIORITY); + MOZ_ASSERT(aThreadPriority >= 0); + + LOG("Setting thread %d to priority level %s; nice level %d", + aTid, ThreadPriorityToString(aThreadPriority), aValue); + int rv = setpriority(PRIO_PROCESS, aTid, aValue); + + if (rv) { + LOG("Failed to set thread %d to priority level %s; error %s", + aTid, ThreadPriorityToString(aThreadPriority), strerror(errno)); + } +} + +static void +SetRealTimeThreadPriority(pid_t aTid, + ThreadPriority aThreadPriority, + int aValue) +{ + int policy = SCHED_FIFO; + + MOZ_ASSERT(aThreadPriority < NUM_THREAD_PRIORITY); + MOZ_ASSERT(aThreadPriority >= 0); + MOZ_ASSERT(IsValidRealTimePriority(aValue, policy), "Invalid real time priority"); + + // Setting real time priorities requires using sched_setscheduler + LOG("Setting thread %d to priority level %s; Real Time priority %d, Schedule FIFO", + aTid, ThreadPriorityToString(aThreadPriority), aValue); + sched_param schedParam; + schedParam.sched_priority = aValue; + int rv = sched_setscheduler(aTid, policy, &schedParam); + + if (rv) { + LOG("Failed to set thread %d to real time priority level %s; error %s", + aTid, ThreadPriorityToString(aThreadPriority), strerror(errno)); + } +} + +static void +SetThreadPriority(pid_t aTid, hal::ThreadPriority aThreadPriority) +{ + // See bug 999115, we can only read preferences on the main thread otherwise + // we create a race condition in HAL + MOZ_ASSERT(NS_IsMainThread(), "Can only set thread priorities on main thread"); + MOZ_ASSERT(aThreadPriority >= 0); + + const char* threadPriorityStr; + switch (aThreadPriority) { + case THREAD_PRIORITY_COMPOSITOR: + threadPriorityStr = ThreadPriorityToString(aThreadPriority); + break; + default: + LOG("Unrecognized thread priority %d; Doing nothing", aThreadPriority); + return; + } + + int realTimePriority = Preferences::GetInt( + nsPrintfCString("hal.gonk.%s.rt_priority", threadPriorityStr).get()); + + if (IsValidRealTimePriority(realTimePriority, SCHED_FIFO)) { + SetRealTimeThreadPriority(aTid, aThreadPriority, realTimePriority); + return; + } + + int niceValue = Preferences::GetInt( + nsPrintfCString("hal.gonk.%s.nice", threadPriorityStr).get()); + + SetThreadNiceValue(aTid, aThreadPriority, niceValue); +} + +namespace { + +/** + * This class sets the priority of threads given the kernel thread's id and a + * value taken from hal::ThreadPriority. + * + * This runnable must always be dispatched to the main thread otherwise it will fail. + * We have to run this from the main thread since preferences can only be read on + * main thread. + */ +class SetThreadPriorityRunnable : public nsRunnable +{ +public: + SetThreadPriorityRunnable(pid_t aThreadId, hal::ThreadPriority aThreadPriority) + : mThreadId(aThreadId) + , mThreadPriority(aThreadPriority) + { } + + NS_IMETHOD Run() + { + NS_ASSERTION(NS_IsMainThread(), "Can only set thread priorities on main thread"); + hal_impl::SetThreadPriority(mThreadId, mThreadPriority); + return NS_OK; + } + +private: + pid_t mThreadId; + hal::ThreadPriority mThreadPriority; +}; + +} // anonymous namespace + +void +SetCurrentThreadPriority(ThreadPriority aThreadPriority) +{ + switch (aThreadPriority) { + case THREAD_PRIORITY_COMPOSITOR: { + pid_t threadId = gettid(); + nsCOMPtr runnable = + new SetThreadPriorityRunnable(threadId, aThreadPriority); + NS_DispatchToMainThread(runnable); + break; + } + default: + LOG("Unrecognized thread priority %d; Doing nothing", aThreadPriority); + return; + } +} + void FactoryReset() { diff --git a/hal/moz.build b/hal/moz.build index f21846124d4c..c65824623274 100644 --- a/hal/moz.build +++ b/hal/moz.build @@ -155,6 +155,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk': 'fallback/FallbackProcessPriority.cpp', 'fallback/FallbackScreenPower.cpp', 'fallback/FallbackSwitch.cpp', + 'fallback/FallbackThreadPriority.cpp', 'fallback/FallbackTime.cpp', 'fallback/FallbackWakeLocks.cpp', ] diff --git a/hal/sandbox/SandboxHal.cpp b/hal/sandbox/SandboxHal.cpp index bd489d26ce63..e027bd88140b 100644 --- a/hal/sandbox/SandboxHal.cpp +++ b/hal/sandbox/SandboxHal.cpp @@ -362,6 +362,12 @@ SetProcessPriority(int aPid, 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."); +} + void EnableFMRadio(const hal::FMRadioSettings& aSettings) {