1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "audio_utils::threads"
18
19 #include <audio_utils/threads.h>
20
21 #include <algorithm> // std::clamp
22 #include <errno.h>
23 #include <sched.h> // scheduler
24 #include <sys/resource.h>
25 #include <thread>
26 #include <utils/Errors.h> // status_t
27 #include <utils/Log.h>
28
29 namespace android::audio_utils {
30
31 /**
32 * Sets the unified priority of the tid.
33 */
set_thread_priority(pid_t tid,int priority)34 status_t set_thread_priority(pid_t tid, int priority) {
35 if (is_realtime_priority(priority)) {
36 // audio processes are designed to work with FIFO, not RR.
37 constexpr int new_policy = SCHED_FIFO;
38 const int rtprio = unified_priority_to_rtprio(priority);
39 struct sched_param param {
40 .sched_priority = rtprio,
41 };
42 if (sched_setscheduler(tid, new_policy, ¶m) != 0) {
43 ALOGW("%s: Cannot set FIFO priority for tid %d to policy %d rtprio %d %s",
44 __func__, tid, new_policy, rtprio, strerror(errno));
45 return -errno;
46 }
47 return NO_ERROR;
48 } else if (is_cfs_priority(priority)) {
49 const int policy = sched_getscheduler(tid);
50 const int nice = unified_priority_to_nice(priority);
51 if (policy != SCHED_OTHER) {
52 struct sched_param param{};
53 constexpr int new_policy = SCHED_OTHER;
54 if (sched_setscheduler(tid, new_policy, ¶m) != 0) {
55 ALOGW("%s: Cannot set CFS priority for tid %d to policy %d nice %d %s",
56 __func__, tid, new_policy, nice, strerror(errno));
57 return -errno;
58 }
59 }
60 if (setpriority(PRIO_PROCESS, tid, nice) != 0) return -errno;
61 return NO_ERROR;
62 } else {
63 return BAD_VALUE;
64 }
65 }
66
67 /**
68 * Returns the unified priority of the tid.
69 *
70 * A negative number represents error.
71 */
get_thread_priority(int tid)72 int get_thread_priority(int tid) {
73 const int policy = sched_getscheduler(tid);
74 if (policy < 0) return -errno;
75
76 if (policy == SCHED_OTHER) {
77 errno = 0; // negative return value valid, so check errno change.
78 const int nice = getpriority(PRIO_PROCESS, tid);
79 if (errno != 0) return -errno;
80 return nice_to_unified_priority(nice);
81 } else if (policy == SCHED_FIFO || policy == SCHED_RR) {
82 struct sched_param param{};
83 if (sched_getparam(tid, ¶m) < 0) return -errno;
84 return rtprio_to_unified_priority(param.sched_priority);
85 } else {
86 return INVALID_OPERATION;
87 }
88 }
89
set_thread_affinity(pid_t tid,const std::bitset<kMaxCpus> & mask)90 status_t set_thread_affinity(pid_t tid, const std::bitset<kMaxCpus>& mask) {
91 cpu_set_t cpuset;
92 CPU_ZERO(&cpuset);
93 const size_t limit = std::min(get_number_cpus(), kMaxCpus);
94 for (size_t i = 0; i < limit; ++i) {
95 if (mask.test(i)) {
96 CPU_SET(i, &cpuset);
97 }
98 }
99 if (sched_setaffinity(tid, sizeof(cpuset), &cpuset) == 0) {
100 return OK;
101 }
102 return -errno;
103 }
104
get_thread_affinity(pid_t tid)105 std::bitset<kMaxCpus> get_thread_affinity(pid_t tid) {
106 cpu_set_t cpuset;
107 CPU_ZERO(&cpuset);
108 std::bitset<kMaxCpus> mask;
109 if (sched_getaffinity(tid, sizeof(cpuset), &cpuset) == 0) {
110 const size_t limit = std::min(get_number_cpus(), kMaxCpus);
111 for (size_t i = 0; i < limit; ++i) {
112 if (CPU_ISSET(i, &cpuset)) {
113 mask.set(i);
114 }
115 }
116 }
117 return mask;
118 }
119
get_cpu()120 int get_cpu() {
121 return sched_getcpu();
122 }
123
124 /*
125 * std::thread::hardware_concurrency() is not optimized. We cache the value here
126 * and it is implementation dependent whether std::thread::hardware_concurrency()
127 * returns only the cpus currently online, or includes offline hot plug cpus.
128 *
129 * See external/libcxx/src/thread.cpp.
130 */
get_number_cpus()131 size_t get_number_cpus() {
132 static constinit std::atomic<size_t> n{}; // zero initialized.
133 size_t value = n.load(std::memory_order_relaxed);
134 if (value == 0) { // not set, so we fetch.
135 value = std::thread::hardware_concurrency();
136 n.store(value, std::memory_order_relaxed); // on race, this store is idempotent.
137 }
138 return value;
139 }
140
141 } // namespace android::audio_utils
142