// 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 #include #include #include #include #include #include #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(&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(&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(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(&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(priority)) forKey:kThreadPriorityKey]; } // static ThreadPriority PlatformThread::GetCurrentThreadPriority() { NSNumber* priority = base::mac::ObjCCast([[[NSThread currentThread] threadDictionary] objectForKey:kThreadPriorityKey]); if (!priority) return ThreadPriority::NORMAL; ThreadPriority thread_priority = static_cast(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(PTHREAD_STACK_MIN)), static_cast(stack_rlimit.rlim_cur)); } return default_stack_size; #endif } void TerminateOnThread() { } } // namespace base