• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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