xref: /aosp_15_r20/system/media/audio_utils/threads.cpp (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
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, &param) != 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, &param) != 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, &param) < 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