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 24namespace base { 25 26namespace { 27NSString* 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 38void 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 52void 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 65namespace { 66 67void 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. 84void 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 165void 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 188ThreadPriority 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 209size_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 244void TerminateOnThread() { 245} 246 247} // namespace base 248