• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/threading/platform_thread.h"
6 
7 #include <errno.h>
8 #include <sched.h>
9 #include <stddef.h>
10 
11 #include "base/files/file_util.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/threading/platform_thread_internal_posix.h"
16 #include "base/threading/thread_id_name_manager.h"
17 #include "build/build_config.h"
18 
19 #if !defined(OS_NACL) && !defined(OS_AIX)
20 #include <pthread.h>
21 #include <sys/prctl.h>
22 #include <sys/resource.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #endif
27 
28 namespace base {
29 namespace {
30 #if !defined(OS_NACL)
31 const FilePath::CharType kCgroupDirectory[] =
32     FILE_PATH_LITERAL("/sys/fs/cgroup");
33 
ThreadPriorityToCgroupDirectory(const FilePath & cgroup_filepath,ThreadPriority priority)34 FilePath ThreadPriorityToCgroupDirectory(const FilePath& cgroup_filepath,
35                                          ThreadPriority priority) {
36   switch (priority) {
37     case ThreadPriority::NORMAL:
38       return cgroup_filepath;
39     case ThreadPriority::BACKGROUND:
40       return cgroup_filepath.Append(FILE_PATH_LITERAL("non-urgent"));
41     case ThreadPriority::DISPLAY:
42     case ThreadPriority::REALTIME_AUDIO:
43       return cgroup_filepath.Append(FILE_PATH_LITERAL("urgent"));
44   }
45   NOTREACHED();
46   return FilePath();
47 }
48 
SetThreadCgroup(PlatformThreadId thread_id,const FilePath & cgroup_directory)49 void SetThreadCgroup(PlatformThreadId thread_id,
50                      const FilePath& cgroup_directory) {
51   FilePath tasks_filepath = cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
52   std::string tid = IntToString(thread_id);
53   int bytes_written = WriteFile(tasks_filepath, tid.c_str(), tid.size());
54   if (bytes_written != static_cast<int>(tid.size())) {
55     DVLOG(1) << "Failed to add " << tid << " to " << tasks_filepath.value();
56   }
57 }
58 
SetThreadCgroupForThreadPriority(PlatformThreadId thread_id,const FilePath & cgroup_filepath,ThreadPriority priority)59 void SetThreadCgroupForThreadPriority(PlatformThreadId thread_id,
60                                       const FilePath& cgroup_filepath,
61                                       ThreadPriority priority) {
62   // Append "chrome" suffix.
63   FilePath cgroup_directory = ThreadPriorityToCgroupDirectory(
64       cgroup_filepath.Append(FILE_PATH_LITERAL("chrome")), priority);
65 
66   // Silently ignore request if cgroup directory doesn't exist.
67   if (!DirectoryExists(cgroup_directory))
68     return;
69 
70   SetThreadCgroup(thread_id, cgroup_directory);
71 }
72 
SetThreadCgroupsForThreadPriority(PlatformThreadId thread_id,ThreadPriority priority)73 void SetThreadCgroupsForThreadPriority(PlatformThreadId thread_id,
74                                        ThreadPriority priority) {
75   FilePath cgroup_filepath(kCgroupDirectory);
76   SetThreadCgroupForThreadPriority(
77       thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("cpuset")), priority);
78   SetThreadCgroupForThreadPriority(
79       thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("schedtune")),
80       priority);
81 }
82 #endif
83 }  // namespace
84 
85 namespace internal {
86 
87 namespace {
88 #if !defined(OS_NACL)
89 const struct sched_param kRealTimePrio = {8};
90 #endif
91 }  // namespace
92 
93 const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
94     {ThreadPriority::BACKGROUND, 10},
95     {ThreadPriority::NORMAL, 0},
96     {ThreadPriority::DISPLAY, -8},
97     {ThreadPriority::REALTIME_AUDIO, -10},
98 };
99 
SetCurrentThreadPriorityForPlatform(ThreadPriority priority)100 bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
101 #if !defined(OS_NACL)
102   SetThreadCgroupsForThreadPriority(PlatformThread::CurrentId(), priority);
103   return priority == ThreadPriority::REALTIME_AUDIO &&
104          pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
105 #else
106   return false;
107 #endif
108 }
109 
GetCurrentThreadPriorityForPlatform(ThreadPriority * priority)110 bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
111 #if !defined(OS_NACL)
112   int maybe_sched_rr = 0;
113   struct sched_param maybe_realtime_prio = {0};
114   if (pthread_getschedparam(pthread_self(), &maybe_sched_rr,
115                             &maybe_realtime_prio) == 0 &&
116       maybe_sched_rr == SCHED_RR &&
117       maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) {
118     *priority = ThreadPriority::REALTIME_AUDIO;
119     return true;
120   }
121 #endif
122   return false;
123 }
124 
125 }  // namespace internal
126 
127 // static
SetName(const std::string & name)128 void PlatformThread::SetName(const std::string& name) {
129   ThreadIdNameManager::GetInstance()->SetName(name);
130 
131 #if !defined(OS_NACL) && !defined(OS_AIX)
132   // On linux we can get the thread names to show up in the debugger by setting
133   // the process name for the LWP.  We don't want to do this for the main
134   // thread because that would rename the process, causing tools like killall
135   // to stop working.
136   if (PlatformThread::CurrentId() == getpid())
137     return;
138 
139   // http://0pointer.de/blog/projects/name-your-threads.html
140   // Set the name for the LWP (which gets truncated to 15 characters).
141   // Note that glibc also has a 'pthread_setname_np' api, but it may not be
142   // available everywhere and it's only benefit over using prctl directly is
143   // that it can set the name of threads other than the current thread.
144   int err = prctl(PR_SET_NAME, name.c_str());
145   // We expect EPERM failures in sandboxed processes, just ignore those.
146   if (err < 0 && errno != EPERM)
147     DPLOG(ERROR) << "prctl(PR_SET_NAME)";
148 #endif  //  !defined(OS_NACL) && !defined(OS_AIX)
149 }
150 
151 #if !defined(OS_NACL) && !defined(OS_AIX)
152 // static
SetThreadPriority(PlatformThreadId thread_id,ThreadPriority priority)153 void PlatformThread::SetThreadPriority(PlatformThreadId thread_id,
154                                        ThreadPriority priority) {
155   // Changing current main threads' priority is not permitted in favor of
156   // security, this interface is restricted to change only non-main thread
157   // priority.
158   CHECK_NE(thread_id, getpid());
159 
160   SetThreadCgroupsForThreadPriority(thread_id, priority);
161 
162   const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
163   if (setpriority(PRIO_PROCESS, thread_id, nice_setting)) {
164     DVPLOG(1) << "Failed to set nice value of thread (" << thread_id << ") to "
165               << nice_setting;
166   }
167 }
168 #endif  //  !defined(OS_NACL) && !defined(OS_AIX)
169 
InitThreading()170 void InitThreading() {}
171 
TerminateOnThread()172 void TerminateOnThread() {}
173 
GetDefaultThreadStackSize(const pthread_attr_t & attributes)174 size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
175 #if !defined(THREAD_SANITIZER)
176   return 0;
177 #else
178   // ThreadSanitizer bloats the stack heavily. Evidence has been that the
179   // default stack size isn't enough for some browser tests.
180   return 2 * (1 << 23);  // 2 times 8192K (the default stack size on Linux).
181 #endif
182 }
183 
184 }  // namespace base
185