#include "base/threading/platform_thread.h"
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <atomic>
#include <cstdint>
#include <optional>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/field_trial_params.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/threading/platform_thread_internal_posix.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_type_delegate.h"
#include "build/build_config.h"
namespace base {
namespace {
ThreadTypeDelegate* g_thread_type_delegate = nullptr;
const FilePath::CharType kCgroupDirectory[] =
FILE_PATH_LITERAL("/sys/fs/cgroup");
FilePath ThreadTypeToCgroupDirectory(const FilePath& cgroup_filepath,
ThreadType thread_type) {
switch (thread_type) {
case ThreadType::kBackground:
case ThreadType::kUtility:
return cgroup_filepath.Append(FILE_PATH_LITERAL("non-urgent"));
case ThreadType::kDefault:
return cgroup_filepath;
case ThreadType::kDisplayCritical:
case ThreadType::kInteractive:
case ThreadType::kRealtimeAudio:
return cgroup_filepath.Append(FILE_PATH_LITERAL("urgent"));
}
NOTREACHED();
}
void SetThreadCgroup(PlatformThreadId thread_id,
const FilePath& cgroup_directory) {
FilePath tasks_filepath = cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
std::string tid = NumberToString(thread_id.raw());
if (!WriteFile(tasks_filepath, as_byte_span(tid))) {
DVLOG(1) << "Failed to add " << tid << " to " << tasks_filepath.value();
}
}
void SetThreadCgroupForThreadType(PlatformThreadId thread_id,
const FilePath& cgroup_filepath,
ThreadType thread_type) {
FilePath cgroup_directory = ThreadTypeToCgroupDirectory(
cgroup_filepath.Append(FILE_PATH_LITERAL("chrome")), thread_type);
if (!DirectoryExists(cgroup_directory)) {
return;
}
SetThreadCgroup(thread_id, cgroup_directory);
}
}
namespace internal {
const ThreadTypeToNiceValuePairForTest kThreadTypeToNiceValueMapForTest[7] = {
{ThreadType::kRealtimeAudio, -10}, {ThreadType::kDisplayCritical, -8},
{ThreadType::kDefault, 0}, {ThreadType::kUtility, 2},
{ThreadType::kBackground, 10},
};
bool CanSetThreadTypeToRealtimeAudio() {
if (geteuid() == 0) {
return true;
}
struct rlimit rlim;
return getrlimit(RLIMIT_RTPRIO, &rlim) != 0 && rlim.rlim_cur != 0;
}
void SetCurrentThreadTypeImpl(ThreadType thread_type,
MessagePumpType pump_type_hint) {
const PlatformThreadId thread_id = PlatformThread::CurrentId();
if (g_thread_type_delegate &&
g_thread_type_delegate->HandleThreadTypeChange(thread_id, thread_type)) {
return;
}
internal::SetThreadType(getpid(), thread_id, thread_type, IsViaIPC(false));
}
std::optional<ThreadType> GetCurrentEffectiveThreadTypeForPlatformForTest() {
int maybe_sched_rr = 0;
struct sched_param maybe_realtime_prio = {0};
if (pthread_getschedparam(pthread_self(), &maybe_sched_rr,
&maybe_realtime_prio) == 0 &&
maybe_sched_rr == SCHED_RR &&
maybe_realtime_prio.sched_priority ==
PlatformThreadLinux::kRealTimeAudioPrio.sched_priority) {
return std::make_optional(ThreadType::kRealtimeAudio);
}
return std::nullopt;
}
PlatformPriorityOverride SetThreadTypeOverride(
PlatformThreadHandle thread_handle,
ThreadType thread_type) {
return false;
}
void RemoveThreadTypeOverrideImpl(
const PlatformPriorityOverride& priority_override_handle,
ThreadType thread_type) {}
}
bool PlatformThreadLinux::IsThreadBackgroundedForTest(
PlatformThreadId thread_id) {
FilePath cgroup_filepath(kCgroupDirectory);
FilePath urgent_cgroup_directory =
cgroup_filepath.Append(FILE_PATH_LITERAL("cpuset"))
.Append(FILE_PATH_LITERAL("chrome"))
.Append(FILE_PATH_LITERAL("urgent"));
FilePath non_urgent_cgroup_directory =
cgroup_filepath.Append(FILE_PATH_LITERAL("cpuset"))
.Append(FILE_PATH_LITERAL("chrome"))
.Append(FILE_PATH_LITERAL("non-urgent"));
if (!DirectoryExists(urgent_cgroup_directory) ||
!DirectoryExists(non_urgent_cgroup_directory)) {
return false;
}
FilePath urgent_tasks_filepath =
urgent_cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
FilePath non_urgent_tasks_filepath =
non_urgent_cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
std::string tid = NumberToString(thread_id.raw());
std::string urgent_tasks;
if (!ReadFileToString(urgent_tasks_filepath, &urgent_tasks)) {
return false;
}
if (urgent_tasks.find(tid) != std::string::npos) {
return false;
}
std::string non_urgent_tasks;
if (!ReadFileToString(non_urgent_tasks_filepath, &non_urgent_tasks)) {
return false;
}
if (non_urgent_tasks.find(tid) != std::string::npos) {
return true;
}
return false;
}
void PlatformThreadBase::SetName(const std::string& name) {
SetNameCommon(name);
if (PlatformThread::CurrentId().raw() == getpid()) {
return;
}
int err = prctl(PR_SET_NAME, name.c_str());
if (err < 0 && errno != EPERM) {
DPLOG(ERROR) << "prctl(PR_SET_NAME)";
}
}
void PlatformThreadLinux::SetThreadTypeDelegate(ThreadTypeDelegate* delegate) {
DCHECK(!g_thread_type_delegate || !delegate);
g_thread_type_delegate = delegate;
}
void PlatformThreadLinux::SetThreadCgroupsForThreadType(
PlatformThreadId thread_id,
ThreadType thread_type) {
FilePath cgroup_filepath(kCgroupDirectory);
SetThreadCgroupForThreadType(
thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("cpuset")),
thread_type);
SetThreadCgroupForThreadType(
thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("schedtune")),
thread_type);
}
void PlatformThreadLinux::SetThreadType(ProcessId process_id,
PlatformThreadId thread_id,
ThreadType thread_type,
IsViaIPC via_ipc) {
internal::SetThreadType(process_id, thread_id, thread_type, via_ipc);
}
namespace internal {
void SetThreadTypeLinux(ProcessId process_id,
PlatformThreadId thread_id,
ThreadType thread_type,
IsViaIPC via_ipc) {
PlatformThreadLinux::SetThreadCgroupsForThreadType(thread_id, thread_type);
PlatformThreadId syscall_tid = thread_id;
if (thread_id == PlatformThreadLinux::CurrentId()) {
syscall_tid = kInvalidThreadId;
}
if (thread_type == ThreadType::kRealtimeAudio) {
if (sched_setscheduler(syscall_tid.raw(), SCHED_RR,
&PlatformThreadLinux::kRealTimeAudioPrio) == 0) {
return;
}
DPLOG(ERROR) << "Failed to set realtime priority for thread " << thread_id;
}
SetThreadNiceFromType(thread_id, thread_type);
}
int ThreadTypeToNiceValue(const ThreadType thread_type) {
switch (thread_type) {
#if BUILDFLAG(ARKWEB_DFX_TRACING)
case ThreadType::kBackground:
return 0;
case ThreadType::kUtility:
return 0;
case ThreadType::kDefault:
return -10;
case ThreadType::kDisplayCritical:
case ThreadType::kInteractive:
return -20;
case ThreadType::kRealtimeAudio:
return -20;
#else
case ThreadType::kBackground:
return 10;
case ThreadType::kUtility:
return 2;
case ThreadType::kDefault:
return 0;
case ThreadType::kDisplayCritical:
case ThreadType::kInteractive:
return -8;
case ThreadType::kRealtimeAudio:
return -10;
#endif
}
}
}
}