123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- // 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 <mach/thread_switch.h>
- #include <stddef.h>
- #include <sys/resource.h>
- #include <algorithm>
- #include <atomic>
- #include "base/feature_list.h"
- #include "base/lazy_instance.h"
- #include "base/logging.h"
- #include "base/mac/foundation_util.h"
- #include "base/mac/mac_util.h"
- #include "base/mac/mach_logging.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/threading/thread_id_name_manager.h"
- #include "base/threading/threading_features.h"
- #include "build/build_config.h"
- namespace base {
- namespace {
- NSString* const kThreadPriorityForTestKey = @"CrThreadPriorityForTestKey";
- NSString* const kRealtimePeriodNsKey = @"CrRealtimePeriodNsKey";
- } // 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]);
- }
- }
- TimeDelta PlatformThread::Delegate::GetRealtimePeriod() {
- return TimeDelta();
- }
- // static
- void PlatformThread::YieldCurrentThread() {
- // Don't use sched_yield(), as it can lead to 10ms delays.
- //
- // This only depresses the thread priority for 1ms, which is more in line
- // with what calling code likely wants. See this bug in webkit for context:
- // https://bugs.webkit.org/show_bug.cgi?id=204871
- mach_msg_timeout_t timeout_ms = 1;
- thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, timeout_ms);
- }
- // 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());
- }
- // Whether optimized realt-time thread config should be used for audio.
- const Feature kOptimizedRealtimeThreadingMac {
- "OptimizedRealtimeThreadingMac",
- #if BUILDFLAG(IS_MAC)
- FEATURE_ENABLED_BY_DEFAULT
- #else
- FEATURE_DISABLED_BY_DEFAULT
- #endif
- };
- namespace {
- bool IsOptimizedRealtimeThreadingMacEnabled() {
- #if BUILDFLAG(IS_MAC)
- // There is some platform bug on 10.14.
- if (mac::IsOS10_14())
- return false;
- #endif
- return FeatureList::IsEnabled(kOptimizedRealtimeThreadingMac);
- }
- } // namespace
- // Fine-tuning optimized realt-time thread config:
- // Whether or not the thread should be preeptible.
- const FeatureParam<bool> kOptimizedRealtimeThreadingMacPreemptible{
- &kOptimizedRealtimeThreadingMac, "preemptible", true};
- // Portion of the time quantum the thread is expected to be busy, (0, 1].
- const FeatureParam<double> kOptimizedRealtimeThreadingMacBusy{
- &kOptimizedRealtimeThreadingMac, "busy", 0.5};
- // Maximum portion of the time quantum the thread is expected to be busy,
- // (kOptimizedRealtimeThreadingMacBusy, 1].
- const FeatureParam<double> kOptimizedRealtimeThreadingMacBusyLimit{
- &kOptimizedRealtimeThreadingMac, "busy_limit", 1.0};
- namespace {
- struct TimeConstraints {
- bool preemptible{kOptimizedRealtimeThreadingMacPreemptible.default_value};
- double busy{kOptimizedRealtimeThreadingMacBusy.default_value};
- double busy_limit{kOptimizedRealtimeThreadingMacBusyLimit.default_value};
- static TimeConstraints ReadFromFeatureParams() {
- double busy_limit = kOptimizedRealtimeThreadingMacBusyLimit.Get();
- return TimeConstraints{
- kOptimizedRealtimeThreadingMacPreemptible.Get(),
- std::min(busy_limit, kOptimizedRealtimeThreadingMacBusy.Get()),
- busy_limit};
- }
- };
- // Use atomics to access FeatureList values when setting up a thread, since
- // there are cases when FeatureList initialization is not synchronized with
- // PlatformThread creation.
- std::atomic<bool> g_use_optimized_realtime_threading(
- kOptimizedRealtimeThreadingMac.default_state == FEATURE_ENABLED_BY_DEFAULT);
- std::atomic<TimeConstraints> g_time_constraints;
- } // namespace
- // static
- void PlatformThread::InitializeOptimizedRealtimeThreadingFeature() {
- // A DCHECK is triggered on FeatureList initialization if the state of a
- // feature has been checked before. To avoid triggering this DCHECK in unit
- // tests that call this before initializing the FeatureList, only check the
- // state of the feature if the FeatureList is initialized.
- if (FeatureList::GetInstance()) {
- g_time_constraints.store(TimeConstraints::ReadFromFeatureParams());
- g_use_optimized_realtime_threading.store(
- IsOptimizedRealtimeThreadingMacEnabled());
- }
- }
- // static
- void PlatformThread::SetCurrentThreadRealtimePeriodValue(
- TimeDelta realtime_period) {
- if (g_use_optimized_realtime_threading.load()) {
- [[NSThread currentThread] threadDictionary][kRealtimePeriodNsKey] =
- @(realtime_period.InNanoseconds());
- }
- }
- namespace {
- TimeDelta GetCurrentThreadRealtimePeriod() {
- NSNumber* period = mac::ObjCCast<NSNumber>(
- [[NSThread currentThread] threadDictionary][kRealtimePeriodNsKey]);
- return period ? Nanoseconds(period.longLongValue) : TimeDelta();
- }
- // Calculates time constrints for THREAD_TIME_CONSTRAINT_POLICY.
- // |realtime_period| is used as a base if it's non-zero.
- // Otherwise we fall back to empirical values.
- thread_time_constraint_policy_data_t GetTimeConstraints(
- TimeDelta realtime_period) {
- thread_time_constraint_policy_data_t time_constraints;
- mach_timebase_info_data_t tb_info;
- mach_timebase_info(&tb_info);
- if (!realtime_period.is_zero()) {
- // Limit the lowest value to 2.9 ms we used to have historically. The lower
- // the period, the more CPU frequency may go up, and we don't want to risk
- // worsening the thermal situation.
- uint32_t abs_realtime_period = saturated_cast<uint32_t>(
- std::max(realtime_period.InNanoseconds(), 2900000LL) *
- (double(tb_info.denom) / tb_info.numer));
- TimeConstraints config = g_time_constraints.load();
- time_constraints.period = abs_realtime_period;
- time_constraints.constraint = std::min(
- abs_realtime_period, uint32_t(abs_realtime_period * config.busy_limit));
- time_constraints.computation =
- std::min(time_constraints.constraint,
- uint32_t(abs_realtime_period * config.busy));
- time_constraints.preemptible = config.preemptible ? YES : NO;
- return time_constraints;
- }
- // Empirical configuration.
- // 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.
- double ms_to_abs_time = double(tb_info.denom) / tb_info.numer * 1000000;
- 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;
- return time_constraints;
- }
- // Enables time-contraint policy and priority suitable for low-latency,
- // glitch-resistant audio.
- void SetPriorityRealtimeAudio(TimeDelta realtime_period) {
- // 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.
- thread_time_constraint_policy_data_t time_constraints =
- GetTimeConstraints(realtime_period);
- 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";
- UmaHistogramCustomMicrosecondsTimes(
- "PlatformThread.Mac.AttemptedRealtimePeriod", realtime_period,
- base::TimeDelta(), base::Milliseconds(100), 100);
- if (result == KERN_SUCCESS) {
- UmaHistogramCustomMicrosecondsTimes(
- "PlatformThread.Mac.SucceededRealtimePeriod", realtime_period,
- base::TimeDelta(), base::Milliseconds(100), 100);
- }
- return;
- }
- } // anonymous namespace
- // static
- bool PlatformThread::CanChangeThreadType(ThreadType from, ThreadType to) {
- return true;
- }
- namespace internal {
- void SetCurrentThreadTypeImpl(ThreadType thread_type,
- MessagePumpType pump_type_hint) {
- // Changing the priority of the main thread causes performance regressions.
- // https://crbug.com/601270
- if ([[NSThread currentThread] isMainThread]) {
- DCHECK(thread_type == ThreadType::kDefault ||
- thread_type == ThreadType::kCompositing);
- return;
- }
- ThreadPriorityForTest priority = ThreadPriorityForTest::kNormal;
- switch (thread_type) {
- case ThreadType::kBackground:
- priority = ThreadPriorityForTest::kBackground;
- [[NSThread currentThread] setThreadPriority:0];
- break;
- case ThreadType::kResourceEfficient:
- case ThreadType::kDefault:
- // TODO(1329208): Experiment with prioritizing kCompositing on Mac like on
- // other platforms.
- [[fallthrough]];
- case ThreadType::kCompositing:
- priority = ThreadPriorityForTest::kNormal;
- [[NSThread currentThread] setThreadPriority:0.5];
- break;
- case ThreadType::kDisplayCritical: {
- // Apple has suggested that insufficient priority may be the reason for
- // Metal shader compilation hangs. A priority of 50 is higher than user
- // input.
- // https://crbug.com/974219.
- priority = ThreadPriorityForTest::kDisplay;
- [[NSThread currentThread] setThreadPriority:1.0];
- sched_param param;
- int policy;
- pthread_t thread = pthread_self();
- if (!pthread_getschedparam(thread, &policy, ¶m)) {
- param.sched_priority = 50;
- pthread_setschedparam(thread, policy, ¶m);
- }
- break;
- }
- case ThreadType::kRealtimeAudio:
- priority = ThreadPriorityForTest::kRealtimeAudio;
- SetPriorityRealtimeAudio(GetCurrentThreadRealtimePeriod());
- DCHECK_EQ([[NSThread currentThread] threadPriority], 1.0);
- break;
- }
- [[NSThread currentThread] threadDictionary][kThreadPriorityForTestKey] =
- @(static_cast<int>(priority));
- }
- } // namespace internal
- // static
- ThreadPriorityForTest PlatformThread::GetCurrentThreadPriorityForTest() {
- NSNumber* priority = base::mac::ObjCCast<NSNumber>(
- [[NSThread currentThread] threadDictionary][kThreadPriorityForTestKey]);
- if (!priority)
- return ThreadPriorityForTest::kNormal;
- ThreadPriorityForTest thread_priority =
- static_cast<ThreadPriorityForTest>(priority.intValue);
- DCHECK_GE(thread_priority, ThreadPriorityForTest::kBackground);
- DCHECK_LE(thread_priority, ThreadPriorityForTest::kMaxValue);
- return thread_priority;
- }
- size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
- #if BUILDFLAG(IS_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
|