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 #import <Foundation/Foundation.h>
      8 #include <mach/mach.h>
      9 #include <mach/mach_time.h>
     10 #include <mach/thread_policy.h>
     11 #include <stddef.h>
     12 #include <sys/resource.h>
     13 
     14 #include <algorithm>
     15 
     16 #include "base/lazy_instance.h"
     17 #include "base/logging.h"
     18 #include "base/mac/foundation_util.h"
     19 #include "base/mac/mach_logging.h"
     20 #include "base/threading/thread_id_name_manager.h"
     21 #include "base/tracked_objects.h"
     22 #include "build/build_config.h"
     23 
     24 namespace base {
     25 
     26 namespace {
     27 NSString* const kThreadPriorityKey = @"CrThreadPriorityKey";
     28 }  // namespace
     29 
     30 // If Cocoa is to be used on more than one thread, it must know that the
     31 // application is multithreaded.  Since it's possible to enter Cocoa code
     32 // from threads created by pthread_thread_create, Cocoa won't necessarily
     33 // be aware that the application is multithreaded.  Spawning an NSThread is
     34 // enough to get Cocoa to set up for multithreaded operation, so this is done
     35 // if necessary before pthread_thread_create spawns any threads.
     36 //
     37 // http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html
     38 void InitThreading() {
     39   static BOOL multithreaded = [NSThread isMultiThreaded];
     40   if (!multithreaded) {
     41     // +[NSObject class] is idempotent.
     42     [NSThread detachNewThreadSelector:@selector(class)
     43                              toTarget:[NSObject class]
     44                            withObject:nil];
     45     multithreaded = YES;
     46 
     47     DCHECK([NSThread isMultiThreaded]);
     48   }
     49 }
     50 
     51 // static
     52 void PlatformThread::SetName(const std::string& name) {
     53   ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
     54   tracked_objects::ThreadData::InitializeThreadContext(name);
     55 
     56   // Mac OS X does not expose the length limit of the name, so
     57   // hardcode it.
     58   const int kMaxNameLength = 63;
     59   std::string shortened_name = name.substr(0, kMaxNameLength);
     60   // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
     61   // See http://crbug.com/47058
     62   pthread_setname_np(shortened_name.c_str());
     63 }
     64 
     65 namespace {
     66 
     67 void SetPriorityNormal(mach_port_t mach_thread_id) {
     68   // Make thread standard policy.
     69   // Please note that this call could fail in rare cases depending
     70   // on runtime conditions.
     71   thread_standard_policy policy;
     72   kern_return_t result =
     73       thread_policy_set(mach_thread_id,
     74                         THREAD_STANDARD_POLICY,
     75                         reinterpret_cast<thread_policy_t>(&policy),
     76                         THREAD_STANDARD_POLICY_COUNT);
     77 
     78   if (result != KERN_SUCCESS)
     79     MACH_DVLOG(1, result) << "thread_policy_set";
     80 }
     81 
     82 // Enables time-contraint policy and priority suitable for low-latency,
     83 // glitch-resistant audio.
     84 void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
     85   // Increase thread priority to real-time.
     86 
     87   // Please note that the thread_policy_set() calls may fail in
     88   // rare cases if the kernel decides the system is under heavy load
     89   // and is unable to handle boosting the thread priority.
     90   // In these cases we just return early and go on with life.
     91 
     92   // Make thread fixed priority.
     93   thread_extended_policy_data_t policy;
     94   policy.timeshare = 0;  // Set to 1 for a non-fixed thread.
     95   kern_return_t result =
     96       thread_policy_set(mach_thread_id,
     97                         THREAD_EXTENDED_POLICY,
     98                         reinterpret_cast<thread_policy_t>(&policy),
     99                         THREAD_EXTENDED_POLICY_COUNT);
    100   if (result != KERN_SUCCESS) {
    101     MACH_DVLOG(1, result) << "thread_policy_set";
    102     return;
    103   }
    104 
    105   // Set to relatively high priority.
    106   thread_precedence_policy_data_t precedence;
    107   precedence.importance = 63;
    108   result = thread_policy_set(mach_thread_id,
    109                              THREAD_PRECEDENCE_POLICY,
    110                              reinterpret_cast<thread_policy_t>(&precedence),
    111                              THREAD_PRECEDENCE_POLICY_COUNT);
    112   if (result != KERN_SUCCESS) {
    113     MACH_DVLOG(1, result) << "thread_policy_set";
    114     return;
    115   }
    116 
    117   // Most important, set real-time constraints.
    118 
    119   // Define the guaranteed and max fraction of time for the audio thread.
    120   // These "duty cycle" values can range from 0 to 1.  A value of 0.5
    121   // means the scheduler would give half the time to the thread.
    122   // These values have empirically been found to yield good behavior.
    123   // Good means that audio performance is high and other threads won't starve.
    124   const double kGuaranteedAudioDutyCycle = 0.75;
    125   const double kMaxAudioDutyCycle = 0.85;
    126 
    127   // Define constants determining how much time the audio thread can
    128   // use in a given time quantum.  All times are in milliseconds.
    129 
    130   // About 128 frames @44.1KHz
    131   const double kTimeQuantum = 2.9;
    132 
    133   // Time guaranteed each quantum.
    134   const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
    135 
    136   // Maximum time each quantum.
    137   const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;
    138 
    139   // Get the conversion factor from milliseconds to absolute time
    140   // which is what the time-constraints call needs.
    141   mach_timebase_info_data_t tb_info;
    142   mach_timebase_info(&tb_info);
    143   double ms_to_abs_time =
    144       (static_cast<double>(tb_info.denom) / tb_info.numer) * 1000000;
    145 
    146   thread_time_constraint_policy_data_t time_constraints;
    147   time_constraints.period = kTimeQuantum * ms_to_abs_time;
    148   time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time;
    149   time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time;
    150   time_constraints.preemptible = 0;
    151 
    152   result =
    153       thread_policy_set(mach_thread_id,
    154                         THREAD_TIME_CONSTRAINT_POLICY,
    155                         reinterpret_cast<thread_policy_t>(&time_constraints),
    156                         THREAD_TIME_CONSTRAINT_POLICY_COUNT);
    157   MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << "thread_policy_set";
    158 
    159   return;
    160 }
    161 
    162 }  // anonymous namespace
    163 
    164 // static
    165 void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
    166   // Convert from pthread_t to mach thread identifier.
    167   mach_port_t mach_thread_id =
    168       pthread_mach_thread_np(PlatformThread::CurrentHandle().platform_handle());
    169 
    170   switch (priority) {
    171     case ThreadPriority::NORMAL:
    172     case ThreadPriority::BACKGROUND:
    173     case ThreadPriority::DISPLAY:
    174       // Add support for non-NORMAL thread priorities. https://crbug.com/554651
    175       SetPriorityNormal(mach_thread_id);
    176       break;
    177     case ThreadPriority::REALTIME_AUDIO:
    178       SetPriorityRealtimeAudio(mach_thread_id);
    179       break;
    180   }
    181 
    182   [[[NSThread currentThread] threadDictionary]
    183       setObject:@(static_cast<int>(priority))
    184          forKey:kThreadPriorityKey];
    185 }
    186 
    187 // static
    188 ThreadPriority PlatformThread::GetCurrentThreadPriority() {
    189   NSNumber* priority = base::mac::ObjCCast<NSNumber>([[[NSThread currentThread]
    190       threadDictionary] objectForKey:kThreadPriorityKey]);
    191 
    192   if (!priority)
    193     return ThreadPriority::NORMAL;
    194 
    195   ThreadPriority thread_priority =
    196       static_cast<ThreadPriority>(priority.intValue);
    197   switch (thread_priority) {
    198     case ThreadPriority::BACKGROUND:
    199     case ThreadPriority::NORMAL:
    200     case ThreadPriority::DISPLAY:
    201     case ThreadPriority::REALTIME_AUDIO:
    202       return thread_priority;
    203     default:
    204       NOTREACHED() << "Unknown priority.";
    205       return ThreadPriority::NORMAL;
    206   }
    207 }
    208 
    209 size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
    210 #if defined(OS_IOS)
    211   return 0;
    212 #else
    213   // The Mac OS X default for a pthread stack size is 512kB.
    214   // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses
    215   // DEFAULT_STACK_SIZE for this purpose.
    216   //
    217   // 512kB isn't quite generous enough for some deeply recursive threads that
    218   // otherwise request the default stack size by specifying 0. Here, adopt
    219   // glibc's behavior as on Linux, which is to use the current stack size
    220   // limit (ulimit -s) as the default stack size. See
    221   // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To
    222   // avoid setting the limit below the Mac OS X default or the minimum usable
    223   // stack size, these values are also considered. If any of these values
    224   // can't be determined, or if stack size is unlimited (ulimit -s unlimited),
    225   // stack_size is left at 0 to get the system default.
    226   //
    227   // Mac OS X normally only applies ulimit -s to the main thread stack. On
    228   // contemporary OS X and Linux systems alike, this value is generally 8MB
    229   // or in that neighborhood.
    230   size_t default_stack_size = 0;
    231   struct rlimit stack_rlimit;
    232   if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 &&
    233       getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 &&
    234       stack_rlimit.rlim_cur != RLIM_INFINITY) {
    235     default_stack_size =
    236         std::max(std::max(default_stack_size,
    237                           static_cast<size_t>(PTHREAD_STACK_MIN)),
    238                  static_cast<size_t>(stack_rlimit.rlim_cur));
    239   }
    240   return default_stack_size;
    241 #endif
    242 }
    243 
    244 void TerminateOnThread() {
    245 }
    246 
    247 }  // namespace base
    248