| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/threading/platform_thread.h" |
| |
| #include <errno.h> |
| #include <pthread.h> |
| #include <stddef.h> |
| #include <sys/prctl.h> |
| #include <sys/resource.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <optional> |
| #include <vector> |
| |
| #include "base/android/android_info.h" |
| #include "base/android/jni_android.h" |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "base/system/sys_info.h" |
| #include "base/threading/platform_thread_internal_posix.h" |
| #include "base/threading/thread_id_name_manager.h" |
| #include "base/trace_event/trace_event.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "base/tasks_jni/ThreadUtils_jni.h" |
| |
| namespace base { |
| |
| // Allows fine-grained control of thread priorities on Android. |
| // Enable with e.g. |
| // --enable-features=AndroidThreadPriority:presentation/-8/default/-1 |
| BASE_FEATURE(kAndroidThreadPriority, base::FEATURE_DISABLED_BY_DEFAULT); |
| BASE_FEATURE_PARAM(int, |
| kBackgroundThreadPriority, |
| &kAndroidThreadPriority, |
| "background", |
| 10); |
| BASE_FEATURE_PARAM(int, |
| kUtilityThreadPriority, |
| &kAndroidThreadPriority, |
| "utility", |
| 1); |
| BASE_FEATURE_PARAM(int, |
| kDefaultThreadPriority, |
| &kAndroidThreadPriority, |
| "default", |
| 0); |
| BASE_FEATURE_PARAM(int, |
| kPresentationThreadPriority, |
| &kAndroidThreadPriority, |
| "presentation", |
| -4); |
| BASE_FEATURE_PARAM(int, |
| kInteractiveThreadPriority, |
| &kAndroidThreadPriority, |
| "interactive", |
| -4); |
| BASE_FEATURE_PARAM(bool, |
| kObeySocRestrictions, |
| &kAndroidThreadPriority, |
| "obey_soc_restrictions", |
| true); |
| // kRealtimeAudio cannot be configured on the command line and should be the |
| // effective maximum. |
| |
| // When enabled, do not run threads with a less important ThreadType than |
| // kDisplayCritical on the big core cluster, for configurations with at least 3 |
| // clusters. This is based on observations that this cluster is both |
| // power-hungry and contended. |
| BASE_FEATURE(kRestrictBigCoreThreadAffinity, base::FEATURE_DISABLED_BY_DEFAULT); |
| |
| namespace { |
| |
| std::vector<uint64_t>* g_max_frequency_per_processor_override = nullptr; |
| |
| struct SetAffinityMask { |
| // Only set when the current CPU configuration has at least 3 separate |
| // clusters. |
| std::optional<cpu_set_t> mask; |
| size_t count = 0; |
| int allowed_count = 0; |
| }; |
| |
| // Get the CPU affinity mask, given the maximum frequency of all cores, and |
| // whether the mask should allow to run on the largest core cluster, for |
| // configurations with at least 3 clusters. |
| // |
| // Returns a value where `mask.mask` is not set if the device is not eligible, |
| // that is it does not have at least 3 different CPU types. |
| SetAffinityMask GetAffinityMask(const std::vector<uint64_t>& max_frequencies, |
| bool can_run) { |
| if (max_frequencies.empty()) { |
| return {}; |
| } |
| |
| auto sorted = max_frequencies; |
| std::sort(sorted.begin(), sorted.end()); |
| uint64_t max_frequency = sorted[sorted.size() - 1]; |
| auto last = std::unique(sorted.begin(), sorted.end()); |
| ssize_t distinct_count = std::distance(sorted.begin(), last); |
| |
| // Don't want to move entirely from big cores on big.LITTLE, only on |
| // little-mid-big designs. |
| if (distinct_count < 3) { |
| return {}; |
| } |
| |
| int allowed_cpus_count = 0; |
| cpu_set_t cpu_set; |
| // SAFETY: Here and below, these are macros that we don't control, and hence |
| // we cannot safely replace. However, CPU_ZERO() is safe, and CPU_SET() has a |
| // check internally to not overflow the bitset, which we repeat in the loop to |
| // be clearer. |
| UNSAFE_BUFFERS(CPU_ZERO(&cpu_set)); |
| for (size_t i = 0; i < max_frequencies.size(); i++) { |
| if (i < CPU_SETSIZE) { |
| if (can_run || (max_frequencies[i] < max_frequency)) { |
| allowed_cpus_count++; |
| UNSAFE_BUFFERS(CPU_SET(i, &cpu_set)); |
| } |
| } |
| } |
| |
| return {cpu_set, max_frequencies.size(), allowed_cpus_count}; |
| } |
| |
| } // namespace |
| |
| void SetMaxFrequencyPerProcessorOverrideForTesting( |
| std::vector<uint64_t>* value) { |
| g_max_frequency_per_processor_override = value; |
| } |
| |
| bool IsEligibleForBigCoreAffinityChange() { |
| if (g_max_frequency_per_processor_override) { |
| return GetAffinityMask(*g_max_frequency_per_processor_override, false) |
| .mask.has_value(); |
| } |
| static const bool eligible = |
| GetAffinityMask(SysInfo::MaxFrequencyPerProcessor(), false) |
| .mask.has_value(); |
| return eligible; |
| } |
| |
| void SetCanRunOnBigCore(PlatformThreadId thread_id, bool can_run) { |
| TRACE_EVENT("base", __PRETTY_FUNCTION__, "thread_id", thread_id, "can_run", |
| can_run); |
| SetAffinityMask mask; |
| if (g_max_frequency_per_processor_override) { |
| mask = GetAffinityMask(*g_max_frequency_per_processor_override, can_run); |
| } else { |
| static const SetAffinityMask all_cores_mask = |
| GetAffinityMask(SysInfo::MaxFrequencyPerProcessor(), true); |
| static const SetAffinityMask no_big_cores_mask = |
| GetAffinityMask(SysInfo::MaxFrequencyPerProcessor(), false); |
| mask = can_run ? all_cores_mask : no_big_cores_mask; |
| } |
| |
| if (!mask.mask) { |
| return; |
| } |
| |
| TRACE_EVENT("base", "SetAffinity", "count", mask.count, "allowed", |
| mask.allowed_count); |
| // If the call fails, it's not a correctness issue. However we want to catch |
| // the sandbox returning EPERM. |
| // |
| // For instance, an invalid mask (e.g. one with an empty intersection with the |
| // set of possible CPUs) returns EINVAL and does not change the current mask, |
| // per sched_setaffinity(2). On more recent kernels, an empty mask resets the |
| // affinity. |
| int retval = |
| sched_setaffinity(thread_id.raw(), sizeof(*mask.mask), &*mask.mask); |
| DPCHECK(!retval); |
| } |
| |
| namespace internal { |
| |
| static int GetAdjustedPresentationThreadPriority() { |
| // ADPF-equipped Google Pixels seem to have issues with input jank if the |
| // kDisplayCriticalThreadPriority value is lowered below -4. |
| static bool is_google_soc = SysInfo::SocManufacturer() == "Google"; |
| const bool allowed_to_lower = |
| (!is_google_soc || !kObeySocRestrictions.Get()) && |
| base::FeatureList::IsEnabled(kAndroidThreadPriority); |
| const int requested_priority = kPresentationThreadPriority.Get(); |
| return allowed_to_lower ? requested_priority |
| : std::max(-4, requested_priority); |
| } |
| |
| // - kRealtimeAudio corresponds to Android's PRIORITY_AUDIO = -16 value. |
| // - kPresentation corresponds to Android's PRIORITY_DISPLAY = -4 value. |
| // - kUtility corresponds to Android's THREAD_PRIORITY_LESS_FAVORABLE = 1 value. |
| // - kBackground corresponds to Android's PRIORITY_BACKGROUND = 10 |
| // value. Contrary to the matching Java APi in Android <13, this does not |
| // restrict the thread to (subset of) little cores. |
| const ThreadTypeToNiceValuePairForTest kThreadTypeToNiceValueMapForTest[7] = { |
| {ThreadType::kRealtimeAudio, -16}, {ThreadType::kPresentation, -4}, |
| {ThreadType::kDefault, 0}, {ThreadType::kUtility, 1}, |
| {ThreadType::kBackground, 10}, |
| }; |
| |
| // If we're not in the AndroidThreadPriorityTrial: |
| // - kBackground corresponds to Android's PRIORITY_BACKGROUND = 10 value. |
| // - kUtility corresponds to Android's THREAD_PRIORITY_LESS_FAVORABLE = 1 |
| // value. |
| // - kPresentation and kInteractive correspond to Android's |
| // PRIORITY_DISPLAY = -4 value. |
| // - kRealtimeAudio corresponds to Android's PRIORITY_AUDIO = -16 value. |
| int ThreadTypeToNiceValue(const ThreadType thread_type) { |
| if (base::FeatureList::IsEnabled(kAndroidThreadPriority)) { |
| [[unlikely]] |
| // Establish weak partial ordering from high to low. |
| DCHECK_LE(kUtilityThreadPriority.Get(), kBackgroundThreadPriority.Get()); |
| DCHECK_LE(kDefaultThreadPriority.Get(), kUtilityThreadPriority.Get()); |
| DCHECK_LE(GetAdjustedPresentationThreadPriority(), |
| kDefaultThreadPriority.Get()); |
| DCHECK_LE(kInteractiveThreadPriority.Get(), |
| GetAdjustedPresentationThreadPriority()); |
| // Check that -16 is the highest priority in the system. |
| DCHECK_LT(-16, kInteractiveThreadPriority.Get()); |
| switch (thread_type) { |
| case ThreadType::kBackground: |
| return kBackgroundThreadPriority.Get(); |
| case ThreadType::kUtility: |
| return kUtilityThreadPriority.Get(); |
| case ThreadType::kDefault: |
| return kDefaultThreadPriority.Get(); |
| case ThreadType::kPresentation: |
| return GetAdjustedPresentationThreadPriority(); |
| case ThreadType::kAudioProcessing: |
| return kInteractiveThreadPriority.Get(); |
| case ThreadType::kRealtimeAudio: |
| // Not configurable, should be the effective highest. |
| return -16; |
| } |
| } |
| switch (thread_type) { |
| case ThreadType::kBackground: |
| return 10; |
| case ThreadType::kUtility: |
| return 1; |
| case ThreadType::kDefault: |
| return 0; |
| case ThreadType::kPresentation: |
| case ThreadType::kAudioProcessing: |
| return -4; |
| case ThreadType::kRealtimeAudio: |
| return -16; |
| } |
| } |
| |
| bool CanSetThreadTypeToRealtimeAudio() { |
| return true; |
| } |
| |
| void SetCurrentThreadTypeImpl(ThreadType thread_type, |
| MessagePumpType pump_type_hint) { |
| // We set the Audio priority through JNI as the Java setThreadPriority will |
| // put it into a preferable cgroup, whereas the "normal" C++ call wouldn't. |
| // However, with |
| // https://android-review.googlesource.com/c/platform/system/core/+/1975808 |
| // this becomes obsolete and we can avoid this starting in API level 33. |
| if (thread_type == ThreadType::kRealtimeAudio && |
| base::android::android_info::sdk_int() < |
| base::android::android_info::SDK_VERSION_T) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_ThreadUtils_setThreadPriorityAudio(env, |
| PlatformThread::CurrentId().raw()); |
| } else if (thread_type == ThreadType::kPresentation && |
| pump_type_hint == MessagePumpType::UI && |
| GetCurrentThreadNiceValue() <= |
| ThreadTypeToNiceValue(ThreadType::kPresentation)) { |
| // Recent versions of Android (O+) up the priority of the UI thread |
| // automatically. |
| } else { |
| SetThreadNiceFromType(PlatformThread::CurrentId(), thread_type); |
| } |
| |
| if (IsEligibleForBigCoreAffinityChange() && |
| base::FeatureList::IsEnabled(kRestrictBigCoreThreadAffinity)) { |
| SetCanRunOnBigCore(PlatformThread::CurrentId(), |
| thread_type >= ThreadType::kPresentation); |
| } |
| } |
| |
| std::optional<ThreadType> GetCurrentEffectiveThreadTypeForPlatformForTest() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| if (Java_ThreadUtils_isThreadPriorityAudio( |
| env, PlatformThread::CurrentId().raw())) { |
| return std::make_optional(ThreadType::kRealtimeAudio); |
| } |
| return std::nullopt; |
| } |
| |
| PlatformPriorityOverride SetThreadTypeOverride( |
| PlatformThreadHandle thread_handle, |
| ThreadType thread_type) { |
| PlatformThreadId thread_id( |
| pthread_gettid_np(thread_handle.platform_handle())); |
| if (GetThreadNiceValue(thread_id) <= ThreadTypeToNiceValue(thread_type)) { |
| return false; |
| } |
| return SetThreadNiceFromType(thread_id, thread_type); |
| } |
| |
| void RemoveThreadTypeOverride( |
| PlatformThreadHandle thread_handle, |
| const PlatformPriorityOverride& priority_override_handle, |
| ThreadType initial_thread_type) { |
| if (!priority_override_handle) { |
| return; |
| } |
| |
| PlatformThreadId thread_id( |
| pthread_gettid_np(thread_handle.platform_handle())); |
| SetThreadNiceFromType(thread_id, initial_thread_type); |
| } |
| |
| } // namespace internal |
| |
| void PlatformThread::SetName(const std::string& name) { |
| SetNameCommon(name); |
| |
| // Like linux, on android we can get the thread names to show up in the |
| // debugger by setting the process name for the LWP. |
| // We don't want to do this for the main thread because that would rename |
| // the process, causing tools like killall to stop working. |
| if (PlatformThread::CurrentId().raw() == getpid()) { |
| return; |
| } |
| |
| // Set the name for the LWP (which gets truncated to 15 characters). |
| int err = prctl(PR_SET_NAME, name.c_str()); |
| if (err < 0 && errno != EPERM) { |
| DPLOG(ERROR) << "prctl(PR_SET_NAME)"; |
| } |
| } |
| |
| void InitThreading() {} |
| |
| void TerminateOnThread() { |
| base::android::DetachFromVM(); |
| } |
| |
| size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { |
| #if !defined(ADDRESS_SANITIZER) |
| return 0; |
| #else |
| // AddressSanitizer bloats the stack approximately 2x. Default stack size of |
| // 1Mb is not enough for some tests (see http://crbug.com/263749 for example). |
| return 2 * (1 << 20); // 2Mb |
| #endif |
| } |
| |
| } // namespace base |
| |
| DEFINE_JNI(ThreadUtils) |