-
Notifications
You must be signed in to change notification settings - Fork 7.1k
/
platform_thread_mac.mm
240 lines (203 loc) · 8.42 KB
/
platform_thread_mac.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// 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