| // 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) |