blob: 96c672023f915af3bf93929e7ed557cc6ba1020c [file] [log] [blame]
// 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 <dlfcn.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/thread_policy.h>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/thread_local.h"
#include "base/tracked_objects.h"
namespace base {
namespace {
LazyInstance<ThreadLocalPointer<char> >::Leaky
current_thread_name = LAZY_INSTANCE_INITIALIZER;
} // 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 char* name) {
current_thread_name.Pointer()->Set(const_cast<char*>(name));
tracked_objects::ThreadData::InitializeThreadContext(name);
// pthread_setname_np is only available in 10.6 or later, so test
// for it at runtime.
int (*dynamic_pthread_setname_np)(const char*);
*reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (!dynamic_pthread_setname_np)
return;
// Mac OS X does not expose the length limit of the name, so
// hardcode it.
const int kMaxNameLength = 63;
std::string shortened_name = std::string(name).substr(0, kMaxNameLength);
// pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
// See http://crbug.com/47058
dynamic_pthread_setname_np(shortened_name.c_str());
}
// static
const char* PlatformThread::GetName() {
return current_thread_name.Pointer()->Get();
}
namespace {
void SetPriorityNormal(mach_port_t mach_thread_id) {
// Make thread standard policy.
// Please note that this call could fail in rare cases depending
// on runtime conditions.
thread_standard_policy policy;
kern_return_t result = thread_policy_set(mach_thread_id,
THREAD_STANDARD_POLICY,
(thread_policy_t)&policy,
THREAD_STANDARD_POLICY_COUNT);
if (result != KERN_SUCCESS)
DVLOG(1) << "thread_policy_set() failure: " << result;
}
// Enables time-contraint policy and priority suitable for low-latency,
// glitch-resistant audio.
void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
kern_return_t result;
// 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.
// Make thread fixed priority.
thread_extended_policy_data_t policy;
policy.timeshare = 0; // Set to 1 for a non-fixed thread.
result = thread_policy_set(mach_thread_id,
THREAD_EXTENDED_POLICY,
(thread_policy_t)&policy,
THREAD_EXTENDED_POLICY_COUNT);
if (result != KERN_SUCCESS) {
DVLOG(1) << "thread_policy_set() failure: " << result;
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,
(thread_policy_t)&precedence,
THREAD_PRECEDENCE_POLICY_COUNT);
if (result != KERN_SUCCESS) {
DVLOG(1) << "thread_policy_set() failure: " << result;
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 =
((double)tb_info.denom / (double)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,
(thread_policy_t)&time_constraints,
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
if (result != KERN_SUCCESS)
DVLOG(1) << "thread_policy_set() failure: " << result;
return;
}
} // anonymous namespace
// static
void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
ThreadPriority priority) {
// Convert from pthread_t to mach thread identifier.
mach_port_t mach_thread_id = pthread_mach_thread_np(handle);
switch (priority) {
case kThreadPriority_Normal:
SetPriorityNormal(mach_thread_id);
break;
case kThreadPriority_RealtimeAudio:
SetPriorityRealtimeAudio(mach_thread_id);
break;
}
}
} // namespace base