| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // 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" | 
 |  | 
 | #import <Foundation/Foundation.h> | 
 | #include <mach/mach.h> | 
 | #include <mach/mach_time.h> | 
 | #include <mach/thread_policy.h> | 
 | #include <stddef.h> | 
 | #include <sys/resource.h> | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "base/lazy_instance.h" | 
 | #include "base/logging.h" | 
 | #include "base/mac/foundation_util.h" | 
 | #include "base/mac/mach_logging.h" | 
 | #include "base/threading/thread_id_name_manager.h" | 
 | #include "build/build_config.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | namespace { | 
 | NSString* const kThreadPriorityKey = @"CrThreadPriorityKey"; | 
 | }  // namespace | 
 |  | 
 | // If Cocoa is to be used on more than one thread, it must know that the | 
 | // application is multithreaded.  Since it's possible to enter Cocoa code | 
 | // from threads created by pthread_thread_create, Cocoa won't necessarily | 
 | // be aware that the application is multithreaded.  Spawning an NSThread is | 
 | // enough to get Cocoa to set up for multithreaded operation, so this is done | 
 | // if necessary before pthread_thread_create spawns any threads. | 
 | // | 
 | // http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html | 
 | void InitThreading() { | 
 |   static BOOL multithreaded = [NSThread isMultiThreaded]; | 
 |   if (!multithreaded) { | 
 |     // +[NSObject class] is idempotent. | 
 |     [NSThread detachNewThreadSelector:@selector(class) | 
 |                              toTarget:[NSObject class] | 
 |                            withObject:nil]; | 
 |     multithreaded = YES; | 
 |  | 
 |     DCHECK([NSThread isMultiThreaded]); | 
 |   } | 
 | } | 
 |  | 
 | // static | 
 | void PlatformThread::SetName(const std::string& name) { | 
 |   ThreadIdNameManager::GetInstance()->SetName(name); | 
 |  | 
 |   // Mac OS X does not expose the length limit of the name, so | 
 |   // hardcode it. | 
 |   const int kMaxNameLength = 63; | 
 |   std::string shortened_name = name.substr(0, kMaxNameLength); | 
 |   // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does. | 
 |   // See http://crbug.com/47058 | 
 |   pthread_setname_np(shortened_name.c_str()); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | // Enables time-contraint policy and priority suitable for low-latency, | 
 | // glitch-resistant audio. | 
 | void SetPriorityRealtimeAudio() { | 
 |   // Increase thread priority to real-time. | 
 |  | 
 |   // Please note that the thread_policy_set() calls may fail in | 
 |   // rare cases if the kernel decides the system is under heavy load | 
 |   // and is unable to handle boosting the thread priority. | 
 |   // In these cases we just return early and go on with life. | 
 |  | 
 |   mach_port_t mach_thread_id = | 
 |       pthread_mach_thread_np(PlatformThread::CurrentHandle().platform_handle()); | 
 |  | 
 |   // Make thread fixed priority. | 
 |   thread_extended_policy_data_t policy; | 
 |   policy.timeshare = 0;  // Set to 1 for a non-fixed thread. | 
 |   kern_return_t result = | 
 |       thread_policy_set(mach_thread_id, | 
 |                         THREAD_EXTENDED_POLICY, | 
 |                         reinterpret_cast<thread_policy_t>(&policy), | 
 |                         THREAD_EXTENDED_POLICY_COUNT); | 
 |   if (result != KERN_SUCCESS) { | 
 |     MACH_DVLOG(1, result) << "thread_policy_set"; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Set to relatively high priority. | 
 |   thread_precedence_policy_data_t precedence; | 
 |   precedence.importance = 63; | 
 |   result = thread_policy_set(mach_thread_id, | 
 |                              THREAD_PRECEDENCE_POLICY, | 
 |                              reinterpret_cast<thread_policy_t>(&precedence), | 
 |                              THREAD_PRECEDENCE_POLICY_COUNT); | 
 |   if (result != KERN_SUCCESS) { | 
 |     MACH_DVLOG(1, result) << "thread_policy_set"; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Most important, set real-time constraints. | 
 |  | 
 |   // Define the guaranteed and max fraction of time for the audio thread. | 
 |   // These "duty cycle" values can range from 0 to 1.  A value of 0.5 | 
 |   // means the scheduler would give half the time to the thread. | 
 |   // These values have empirically been found to yield good behavior. | 
 |   // Good means that audio performance is high and other threads won't starve. | 
 |   const double kGuaranteedAudioDutyCycle = 0.75; | 
 |   const double kMaxAudioDutyCycle = 0.85; | 
 |  | 
 |   // Define constants determining how much time the audio thread can | 
 |   // use in a given time quantum.  All times are in milliseconds. | 
 |  | 
 |   // About 128 frames @44.1KHz | 
 |   const double kTimeQuantum = 2.9; | 
 |  | 
 |   // Time guaranteed each quantum. | 
 |   const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum; | 
 |  | 
 |   // Maximum time each quantum. | 
 |   const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum; | 
 |  | 
 |   // Get the conversion factor from milliseconds to absolute time | 
 |   // which is what the time-constraints call needs. | 
 |   mach_timebase_info_data_t tb_info; | 
 |   mach_timebase_info(&tb_info); | 
 |   double ms_to_abs_time = | 
 |       (static_cast<double>(tb_info.denom) / tb_info.numer) * 1000000; | 
 |  | 
 |   thread_time_constraint_policy_data_t time_constraints; | 
 |   time_constraints.period = kTimeQuantum * ms_to_abs_time; | 
 |   time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time; | 
 |   time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time; | 
 |   time_constraints.preemptible = 0; | 
 |  | 
 |   result = | 
 |       thread_policy_set(mach_thread_id, | 
 |                         THREAD_TIME_CONSTRAINT_POLICY, | 
 |                         reinterpret_cast<thread_policy_t>(&time_constraints), | 
 |                         THREAD_TIME_CONSTRAINT_POLICY_COUNT); | 
 |   MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << "thread_policy_set"; | 
 |  | 
 |   return; | 
 | } | 
 |  | 
 | }  // anonymous namespace | 
 |  | 
 | // static | 
 | bool PlatformThread::CanIncreaseCurrentThreadPriority() { | 
 |   return true; | 
 | } | 
 |  | 
 | // static | 
 | void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { | 
 |   // Changing the priority of the main thread causes performance regressions. | 
 |   // https://crbug.com/601270 | 
 |   DCHECK(![[NSThread currentThread] isMainThread]); | 
 |  | 
 |   switch (priority) { | 
 |     case ThreadPriority::BACKGROUND: | 
 |       [[NSThread currentThread] setThreadPriority:0]; | 
 |       break; | 
 |     case ThreadPriority::NORMAL: | 
 |     case ThreadPriority::DISPLAY: | 
 |       [[NSThread currentThread] setThreadPriority:0.5]; | 
 |       break; | 
 |     case ThreadPriority::REALTIME_AUDIO: | 
 |       SetPriorityRealtimeAudio(); | 
 |       DCHECK_EQ([[NSThread currentThread] threadPriority], 1.0); | 
 |       break; | 
 |   } | 
 |  | 
 |   [[[NSThread currentThread] threadDictionary] | 
 |       setObject:@(static_cast<int>(priority)) | 
 |          forKey:kThreadPriorityKey]; | 
 | } | 
 |  | 
 | // static | 
 | ThreadPriority PlatformThread::GetCurrentThreadPriority() { | 
 |   NSNumber* priority = base::mac::ObjCCast<NSNumber>([[[NSThread currentThread] | 
 |       threadDictionary] objectForKey:kThreadPriorityKey]); | 
 |  | 
 |   if (!priority) | 
 |     return ThreadPriority::NORMAL; | 
 |  | 
 |   ThreadPriority thread_priority = | 
 |       static_cast<ThreadPriority>(priority.intValue); | 
 |   switch (thread_priority) { | 
 |     case ThreadPriority::BACKGROUND: | 
 |     case ThreadPriority::NORMAL: | 
 |     case ThreadPriority::DISPLAY: | 
 |     case ThreadPriority::REALTIME_AUDIO: | 
 |       return thread_priority; | 
 |     default: | 
 |       NOTREACHED() << "Unknown priority."; | 
 |       return ThreadPriority::NORMAL; | 
 |   } | 
 | } | 
 |  | 
 | size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { | 
 | #if defined(OS_IOS) | 
 |   return 0; | 
 | #else | 
 |   // The Mac OS X default for a pthread stack size is 512kB. | 
 |   // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses | 
 |   // DEFAULT_STACK_SIZE for this purpose. | 
 |   // | 
 |   // 512kB isn't quite generous enough for some deeply recursive threads that | 
 |   // otherwise request the default stack size by specifying 0. Here, adopt | 
 |   // glibc's behavior as on Linux, which is to use the current stack size | 
 |   // limit (ulimit -s) as the default stack size. See | 
 |   // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To | 
 |   // avoid setting the limit below the Mac OS X default or the minimum usable | 
 |   // stack size, these values are also considered. If any of these values | 
 |   // can't be determined, or if stack size is unlimited (ulimit -s unlimited), | 
 |   // stack_size is left at 0 to get the system default. | 
 |   // | 
 |   // Mac OS X normally only applies ulimit -s to the main thread stack. On | 
 |   // contemporary OS X and Linux systems alike, this value is generally 8MB | 
 |   // or in that neighborhood. | 
 |   size_t default_stack_size = 0; | 
 |   struct rlimit stack_rlimit; | 
 |   if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 && | 
 |       getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 && | 
 |       stack_rlimit.rlim_cur != RLIM_INFINITY) { | 
 |     default_stack_size = | 
 |         std::max(std::max(default_stack_size, | 
 |                           static_cast<size_t>(PTHREAD_STACK_MIN)), | 
 |                  static_cast<size_t>(stack_rlimit.rlim_cur)); | 
 |   } | 
 |   return default_stack_size; | 
 | #endif | 
 | } | 
 |  | 
 | void TerminateOnThread() { | 
 | } | 
 |  | 
 | }  // namespace base |