| // Copyright 2016 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/shared/pthread/thread_create_priority.h" |
| |
| #include <sched.h> |
| #include <sys/resource.h> |
| |
| #include "starboard/common/log.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace pthread { |
| |
| // This is the maximum priority that will be passed to SetRoundRobinScheduler(). |
| const int kMaxRoundRobinPriority = 2; |
| |
| // Permissions are needed to allow usage non-default thread schedulers and |
| // thread priorities. Specifically, /etc/security/limits.conf should set |
| // "rtprio" and "nice" limits. "rtprio" will allow use of the real-time |
| // schedulers, and "nice" will allow use of nice priorities as well as allow |
| // SCHED_IDLE threads to increase their priority. If the user is 'pi', then |
| // limits.conf should have the following lines: |
| // @pi hard rtprio 99 |
| // @pi soft rtprio 99 |
| // @pi hard nice -20 |
| // @pi soft nice -20 |
| const char kSchedulerErrorMessage[] = |
| "Unable to set scheduler. Please update " |
| "limits.conf to set 'rtprio' limit to 99 and 'nice' limit to -20."; |
| |
| // Note that use of sched_setscheduler() has been found to be more reliably |
| // supported than pthread_setschedparam(), so we are using that. |
| |
| void SetIdleScheduler() { |
| struct sched_param thread_sched_param; |
| thread_sched_param.sched_priority = 0; |
| int result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param); |
| SB_CHECK(result == 0) << kSchedulerErrorMessage; |
| } |
| |
| void SetOtherScheduler() { |
| struct sched_param thread_sched_param; |
| thread_sched_param.sched_priority = 0; |
| int result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param); |
| SB_CHECK(result == 0) << kSchedulerErrorMessage; |
| } |
| |
| // Here |priority| is a number >= 0, where the higher the number, the |
| // higher the priority. |
| // |
| // If real time priorities are not allowed, then fall back to SCHED_OTHER. |
| // Otherwise, use SCHED_RR with the desired priority (or RLIMIT_RTPRIO -- |
| // whichever is lower). |
| void SetRoundRobinScheduler(int priority) { |
| // Determine the minimum and maximum priorities that will be used. |
| int min_priority = sched_get_priority_min(SCHED_RR); |
| int max_used_priority = min_priority + kMaxRoundRobinPriority; |
| |
| struct rlimit rlimit_rtprio; |
| getrlimit(RLIMIT_RTPRIO, &rlimit_rtprio); |
| |
| if (rlimit_rtprio.rlim_cur < min_priority) { |
| // Can't use the real-time scheduler at all. Use SCHED_OTHER instead. |
| // Performance will be noticeably worse than ideal. |
| SB_LOG(ERROR) << "Unable to use real-time round-robin scheduler because " |
| << "the maximum real-time scheduling priority is too low (" |
| << rlimit_rtprio.rlim_cur << " < " << max_used_priority |
| << "). Update setting using `ulimit -r` or limits.conf file."; |
| SetOtherScheduler(); |
| return; |
| } |
| |
| if (rlimit_rtprio.rlim_cur < max_used_priority) { |
| // The maximum desired priority will be clamped. |
| // Performance will be slightly worse than ideal. |
| SB_LOG(WARNING) << "The maximum real-time scheduling priority is too low (" |
| << rlimit_rtprio.rlim_cur << " < " << max_used_priority |
| << "). Update setting using `ulimit -r` or limits.conf " |
| << "file."; |
| } |
| |
| struct sched_param thread_sched_param; |
| thread_sched_param.sched_priority = std::min( |
| min_priority + priority, static_cast<int>(rlimit_rtprio.rlim_cur)); |
| int result = sched_setscheduler(0, SCHED_RR, &thread_sched_param); |
| SB_CHECK(result == 0) << kSchedulerErrorMessage; |
| } |
| |
| void ThreadSetPriority(SbThreadPriority priority) { |
| if (!kSbHasThreadPrioritySupport) |
| return; |
| |
| // Use different schedulers according to priority. This is preferred over |
| // using SCHED_RR for all threads because the scheduler time slice is too |
| // high (defaults to 100ms) for the desired threading behavior. |
| switch (priority) { |
| case kSbThreadPriorityLowest: |
| case kSbThreadPriorityLow: |
| SetIdleScheduler(); |
| break; |
| case kSbThreadNoPriority: |
| case kSbThreadPriorityNormal: |
| SetOtherScheduler(); |
| break; |
| case kSbThreadPriorityHigh: |
| SetRoundRobinScheduler(0); |
| break; |
| case kSbThreadPriorityHighest: |
| SetRoundRobinScheduler(1); |
| break; |
| case kSbThreadPriorityRealTime: |
| SetRoundRobinScheduler(kMaxRoundRobinPriority); |
| break; |
| default: |
| SB_NOTREACHED(); |
| break; |
| } |
| } |
| |
| } // namespace pthread |
| } // namespace shared |
| } // namespace starboard |
| |
| bool SbThreadSetPriority(SbThreadPriority priority) { |
| ::starboard::shared::pthread::ThreadSetPriority(priority); |
| return true; |
| } |
| |
| bool SbThreadGetPriority(SbThreadPriority* priority) { |
| int scheduler = sched_getscheduler(0); |
| if (scheduler == -1) { |
| return false; |
| } |
| switch (scheduler) { |
| case SCHED_IDLE: |
| *priority = kSbThreadPriorityLowest; |
| return true; |
| case SCHED_OTHER: |
| *priority = kSbThreadPriorityNormal; |
| return true; |
| } |
| if (scheduler == SCHED_RR) { |
| struct sched_param param; |
| int res = sched_getparam(0, ¶m); |
| if (res == -1) { |
| return false; |
| } |
| switch (param.sched_priority) { |
| case 0: |
| *priority = kSbThreadPriorityHigh; |
| return true; |
| case 1: |
| *priority = kSbThreadPriorityHighest; |
| return true; |
| case 2: |
| *priority = kSbThreadPriorityRealTime; |
| return true; |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |