Home | History | Annotate | Download | only in threading
      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 
     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 
     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 
     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 
     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 
    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 
    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
    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
    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 
    170 void InitThreading() {}
    171 
    172 void TerminateOnThread() {}
    173 
    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