platform_thread_mac.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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. #include "base/threading/platform_thread.h"
  5. #import <Foundation/Foundation.h>
  6. #include <mach/mach.h>
  7. #include <mach/mach_time.h>
  8. #include <mach/thread_policy.h>
  9. #include <mach/thread_switch.h>
  10. #include <stddef.h>
  11. #include <sys/resource.h>
  12. #include <algorithm>
  13. #include <atomic>
  14. #include "base/feature_list.h"
  15. #include "base/lazy_instance.h"
  16. #include "base/logging.h"
  17. #include "base/mac/foundation_util.h"
  18. #include "base/mac/mac_util.h"
  19. #include "base/mac/mach_logging.h"
  20. #include "base/metrics/histogram_functions.h"
  21. #include "base/threading/thread_id_name_manager.h"
  22. #include "base/threading/threading_features.h"
  23. #include "build/build_config.h"
  24. namespace base {
  25. namespace {
  26. NSString* const kThreadPriorityForTestKey = @"CrThreadPriorityForTestKey";
  27. NSString* const kRealtimePeriodNsKey = @"CrRealtimePeriodNsKey";
  28. } // namespace
  29. // If Cocoa is to be used on more than one thread, it must know that the
  30. // application is multithreaded. Since it's possible to enter Cocoa code
  31. // from threads created by pthread_thread_create, Cocoa won't necessarily
  32. // be aware that the application is multithreaded. Spawning an NSThread is
  33. // enough to get Cocoa to set up for multithreaded operation, so this is done
  34. // if necessary before pthread_thread_create spawns any threads.
  35. //
  36. // http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html
  37. void InitThreading() {
  38. static BOOL multithreaded = [NSThread isMultiThreaded];
  39. if (!multithreaded) {
  40. // +[NSObject class] is idempotent.
  41. [NSThread detachNewThreadSelector:@selector(class)
  42. toTarget:[NSObject class]
  43. withObject:nil];
  44. multithreaded = YES;
  45. DCHECK([NSThread isMultiThreaded]);
  46. }
  47. }
  48. TimeDelta PlatformThread::Delegate::GetRealtimePeriod() {
  49. return TimeDelta();
  50. }
  51. // static
  52. void PlatformThread::YieldCurrentThread() {
  53. // Don't use sched_yield(), as it can lead to 10ms delays.
  54. //
  55. // This only depresses the thread priority for 1ms, which is more in line
  56. // with what calling code likely wants. See this bug in webkit for context:
  57. // https://bugs.webkit.org/show_bug.cgi?id=204871
  58. mach_msg_timeout_t timeout_ms = 1;
  59. thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, timeout_ms);
  60. }
  61. // static
  62. void PlatformThread::SetName(const std::string& name) {
  63. ThreadIdNameManager::GetInstance()->SetName(name);
  64. // Mac OS X does not expose the length limit of the name, so
  65. // hardcode it.
  66. const int kMaxNameLength = 63;
  67. std::string shortened_name = name.substr(0, kMaxNameLength);
  68. // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
  69. // See http://crbug.com/47058
  70. pthread_setname_np(shortened_name.c_str());
  71. }
  72. // Whether optimized realt-time thread config should be used for audio.
  73. const Feature kOptimizedRealtimeThreadingMac {
  74. "OptimizedRealtimeThreadingMac",
  75. #if BUILDFLAG(IS_MAC)
  76. FEATURE_ENABLED_BY_DEFAULT
  77. #else
  78. FEATURE_DISABLED_BY_DEFAULT
  79. #endif
  80. };
  81. namespace {
  82. bool IsOptimizedRealtimeThreadingMacEnabled() {
  83. #if BUILDFLAG(IS_MAC)
  84. // There is some platform bug on 10.14.
  85. if (mac::IsOS10_14())
  86. return false;
  87. #endif
  88. return FeatureList::IsEnabled(kOptimizedRealtimeThreadingMac);
  89. }
  90. } // namespace
  91. // Fine-tuning optimized realt-time thread config:
  92. // Whether or not the thread should be preeptible.
  93. const FeatureParam<bool> kOptimizedRealtimeThreadingMacPreemptible{
  94. &kOptimizedRealtimeThreadingMac, "preemptible", true};
  95. // Portion of the time quantum the thread is expected to be busy, (0, 1].
  96. const FeatureParam<double> kOptimizedRealtimeThreadingMacBusy{
  97. &kOptimizedRealtimeThreadingMac, "busy", 0.5};
  98. // Maximum portion of the time quantum the thread is expected to be busy,
  99. // (kOptimizedRealtimeThreadingMacBusy, 1].
  100. const FeatureParam<double> kOptimizedRealtimeThreadingMacBusyLimit{
  101. &kOptimizedRealtimeThreadingMac, "busy_limit", 1.0};
  102. namespace {
  103. struct TimeConstraints {
  104. bool preemptible{kOptimizedRealtimeThreadingMacPreemptible.default_value};
  105. double busy{kOptimizedRealtimeThreadingMacBusy.default_value};
  106. double busy_limit{kOptimizedRealtimeThreadingMacBusyLimit.default_value};
  107. static TimeConstraints ReadFromFeatureParams() {
  108. double busy_limit = kOptimizedRealtimeThreadingMacBusyLimit.Get();
  109. return TimeConstraints{
  110. kOptimizedRealtimeThreadingMacPreemptible.Get(),
  111. std::min(busy_limit, kOptimizedRealtimeThreadingMacBusy.Get()),
  112. busy_limit};
  113. }
  114. };
  115. // Use atomics to access FeatureList values when setting up a thread, since
  116. // there are cases when FeatureList initialization is not synchronized with
  117. // PlatformThread creation.
  118. std::atomic<bool> g_use_optimized_realtime_threading(
  119. kOptimizedRealtimeThreadingMac.default_state == FEATURE_ENABLED_BY_DEFAULT);
  120. std::atomic<TimeConstraints> g_time_constraints;
  121. } // namespace
  122. // static
  123. void PlatformThread::InitializeOptimizedRealtimeThreadingFeature() {
  124. // A DCHECK is triggered on FeatureList initialization if the state of a
  125. // feature has been checked before. To avoid triggering this DCHECK in unit
  126. // tests that call this before initializing the FeatureList, only check the
  127. // state of the feature if the FeatureList is initialized.
  128. if (FeatureList::GetInstance()) {
  129. g_time_constraints.store(TimeConstraints::ReadFromFeatureParams());
  130. g_use_optimized_realtime_threading.store(
  131. IsOptimizedRealtimeThreadingMacEnabled());
  132. }
  133. }
  134. // static
  135. void PlatformThread::SetCurrentThreadRealtimePeriodValue(
  136. TimeDelta realtime_period) {
  137. if (g_use_optimized_realtime_threading.load()) {
  138. [[NSThread currentThread] threadDictionary][kRealtimePeriodNsKey] =
  139. @(realtime_period.InNanoseconds());
  140. }
  141. }
  142. namespace {
  143. TimeDelta GetCurrentThreadRealtimePeriod() {
  144. NSNumber* period = mac::ObjCCast<NSNumber>(
  145. [[NSThread currentThread] threadDictionary][kRealtimePeriodNsKey]);
  146. return period ? Nanoseconds(period.longLongValue) : TimeDelta();
  147. }
  148. // Calculates time constrints for THREAD_TIME_CONSTRAINT_POLICY.
  149. // |realtime_period| is used as a base if it's non-zero.
  150. // Otherwise we fall back to empirical values.
  151. thread_time_constraint_policy_data_t GetTimeConstraints(
  152. TimeDelta realtime_period) {
  153. thread_time_constraint_policy_data_t time_constraints;
  154. mach_timebase_info_data_t tb_info;
  155. mach_timebase_info(&tb_info);
  156. if (!realtime_period.is_zero()) {
  157. // Limit the lowest value to 2.9 ms we used to have historically. The lower
  158. // the period, the more CPU frequency may go up, and we don't want to risk
  159. // worsening the thermal situation.
  160. uint32_t abs_realtime_period = saturated_cast<uint32_t>(
  161. std::max(realtime_period.InNanoseconds(), 2900000LL) *
  162. (double(tb_info.denom) / tb_info.numer));
  163. TimeConstraints config = g_time_constraints.load();
  164. time_constraints.period = abs_realtime_period;
  165. time_constraints.constraint = std::min(
  166. abs_realtime_period, uint32_t(abs_realtime_period * config.busy_limit));
  167. time_constraints.computation =
  168. std::min(time_constraints.constraint,
  169. uint32_t(abs_realtime_period * config.busy));
  170. time_constraints.preemptible = config.preemptible ? YES : NO;
  171. return time_constraints;
  172. }
  173. // Empirical configuration.
  174. // Define the guaranteed and max fraction of time for the audio thread.
  175. // These "duty cycle" values can range from 0 to 1. A value of 0.5
  176. // means the scheduler would give half the time to the thread.
  177. // These values have empirically been found to yield good behavior.
  178. // Good means that audio performance is high and other threads won't starve.
  179. const double kGuaranteedAudioDutyCycle = 0.75;
  180. const double kMaxAudioDutyCycle = 0.85;
  181. // Define constants determining how much time the audio thread can
  182. // use in a given time quantum. All times are in milliseconds.
  183. // About 128 frames @44.1KHz
  184. const double kTimeQuantum = 2.9;
  185. // Time guaranteed each quantum.
  186. const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
  187. // Maximum time each quantum.
  188. const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;
  189. // Get the conversion factor from milliseconds to absolute time
  190. // which is what the time-constraints call needs.
  191. double ms_to_abs_time = double(tb_info.denom) / tb_info.numer * 1000000;
  192. time_constraints.period = kTimeQuantum * ms_to_abs_time;
  193. time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time;
  194. time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time;
  195. time_constraints.preemptible = 0;
  196. return time_constraints;
  197. }
  198. // Enables time-contraint policy and priority suitable for low-latency,
  199. // glitch-resistant audio.
  200. void SetPriorityRealtimeAudio(TimeDelta realtime_period) {
  201. // Increase thread priority to real-time.
  202. // Please note that the thread_policy_set() calls may fail in
  203. // rare cases if the kernel decides the system is under heavy load
  204. // and is unable to handle boosting the thread priority.
  205. // In these cases we just return early and go on with life.
  206. mach_port_t mach_thread_id =
  207. pthread_mach_thread_np(PlatformThread::CurrentHandle().platform_handle());
  208. // Make thread fixed priority.
  209. thread_extended_policy_data_t policy;
  210. policy.timeshare = 0; // Set to 1 for a non-fixed thread.
  211. kern_return_t result = thread_policy_set(
  212. mach_thread_id, THREAD_EXTENDED_POLICY,
  213. reinterpret_cast<thread_policy_t>(&policy), THREAD_EXTENDED_POLICY_COUNT);
  214. if (result != KERN_SUCCESS) {
  215. MACH_DVLOG(1, result) << "thread_policy_set";
  216. return;
  217. }
  218. // Set to relatively high priority.
  219. thread_precedence_policy_data_t precedence;
  220. precedence.importance = 63;
  221. result = thread_policy_set(mach_thread_id, THREAD_PRECEDENCE_POLICY,
  222. reinterpret_cast<thread_policy_t>(&precedence),
  223. THREAD_PRECEDENCE_POLICY_COUNT);
  224. if (result != KERN_SUCCESS) {
  225. MACH_DVLOG(1, result) << "thread_policy_set";
  226. return;
  227. }
  228. // Most important, set real-time constraints.
  229. thread_time_constraint_policy_data_t time_constraints =
  230. GetTimeConstraints(realtime_period);
  231. result =
  232. thread_policy_set(mach_thread_id, THREAD_TIME_CONSTRAINT_POLICY,
  233. reinterpret_cast<thread_policy_t>(&time_constraints),
  234. THREAD_TIME_CONSTRAINT_POLICY_COUNT);
  235. MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << "thread_policy_set";
  236. UmaHistogramCustomMicrosecondsTimes(
  237. "PlatformThread.Mac.AttemptedRealtimePeriod", realtime_period,
  238. base::TimeDelta(), base::Milliseconds(100), 100);
  239. if (result == KERN_SUCCESS) {
  240. UmaHistogramCustomMicrosecondsTimes(
  241. "PlatformThread.Mac.SucceededRealtimePeriod", realtime_period,
  242. base::TimeDelta(), base::Milliseconds(100), 100);
  243. }
  244. return;
  245. }
  246. } // anonymous namespace
  247. // static
  248. bool PlatformThread::CanChangeThreadType(ThreadType from, ThreadType to) {
  249. return true;
  250. }
  251. namespace internal {
  252. void SetCurrentThreadTypeImpl(ThreadType thread_type,
  253. MessagePumpType pump_type_hint) {
  254. // Changing the priority of the main thread causes performance regressions.
  255. // https://crbug.com/601270
  256. if ([[NSThread currentThread] isMainThread]) {
  257. DCHECK(thread_type == ThreadType::kDefault ||
  258. thread_type == ThreadType::kCompositing);
  259. return;
  260. }
  261. ThreadPriorityForTest priority = ThreadPriorityForTest::kNormal;
  262. switch (thread_type) {
  263. case ThreadType::kBackground:
  264. priority = ThreadPriorityForTest::kBackground;
  265. [[NSThread currentThread] setThreadPriority:0];
  266. break;
  267. case ThreadType::kResourceEfficient:
  268. case ThreadType::kDefault:
  269. // TODO(1329208): Experiment with prioritizing kCompositing on Mac like on
  270. // other platforms.
  271. [[fallthrough]];
  272. case ThreadType::kCompositing:
  273. priority = ThreadPriorityForTest::kNormal;
  274. [[NSThread currentThread] setThreadPriority:0.5];
  275. break;
  276. case ThreadType::kDisplayCritical: {
  277. // Apple has suggested that insufficient priority may be the reason for
  278. // Metal shader compilation hangs. A priority of 50 is higher than user
  279. // input.
  280. // https://crbug.com/974219.
  281. priority = ThreadPriorityForTest::kDisplay;
  282. [[NSThread currentThread] setThreadPriority:1.0];
  283. sched_param param;
  284. int policy;
  285. pthread_t thread = pthread_self();
  286. if (!pthread_getschedparam(thread, &policy, &param)) {
  287. param.sched_priority = 50;
  288. pthread_setschedparam(thread, policy, &param);
  289. }
  290. break;
  291. }
  292. case ThreadType::kRealtimeAudio:
  293. priority = ThreadPriorityForTest::kRealtimeAudio;
  294. SetPriorityRealtimeAudio(GetCurrentThreadRealtimePeriod());
  295. DCHECK_EQ([[NSThread currentThread] threadPriority], 1.0);
  296. break;
  297. }
  298. [[NSThread currentThread] threadDictionary][kThreadPriorityForTestKey] =
  299. @(static_cast<int>(priority));
  300. }
  301. } // namespace internal
  302. // static
  303. ThreadPriorityForTest PlatformThread::GetCurrentThreadPriorityForTest() {
  304. NSNumber* priority = base::mac::ObjCCast<NSNumber>(
  305. [[NSThread currentThread] threadDictionary][kThreadPriorityForTestKey]);
  306. if (!priority)
  307. return ThreadPriorityForTest::kNormal;
  308. ThreadPriorityForTest thread_priority =
  309. static_cast<ThreadPriorityForTest>(priority.intValue);
  310. DCHECK_GE(thread_priority, ThreadPriorityForTest::kBackground);
  311. DCHECK_LE(thread_priority, ThreadPriorityForTest::kMaxValue);
  312. return thread_priority;
  313. }
  314. size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
  315. #if BUILDFLAG(IS_IOS)
  316. return 0;
  317. #else
  318. // The Mac OS X default for a pthread stack size is 512kB.
  319. // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses
  320. // DEFAULT_STACK_SIZE for this purpose.
  321. //
  322. // 512kB isn't quite generous enough for some deeply recursive threads that
  323. // otherwise request the default stack size by specifying 0. Here, adopt
  324. // glibc's behavior as on Linux, which is to use the current stack size
  325. // limit (ulimit -s) as the default stack size. See
  326. // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To
  327. // avoid setting the limit below the Mac OS X default or the minimum usable
  328. // stack size, these values are also considered. If any of these values
  329. // can't be determined, or if stack size is unlimited (ulimit -s unlimited),
  330. // stack_size is left at 0 to get the system default.
  331. //
  332. // Mac OS X normally only applies ulimit -s to the main thread stack. On
  333. // contemporary OS X and Linux systems alike, this value is generally 8MB
  334. // or in that neighborhood.
  335. size_t default_stack_size = 0;
  336. struct rlimit stack_rlimit;
  337. if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 &&
  338. getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 &&
  339. stack_rlimit.rlim_cur != RLIM_INFINITY) {
  340. default_stack_size =
  341. std::max(std::max(default_stack_size,
  342. static_cast<size_t>(PTHREAD_STACK_MIN)),
  343. static_cast<size_t>(stack_rlimit.rlim_cur));
  344. }
  345. return default_stack_size;
  346. #endif
  347. }
  348. void TerminateOnThread() {
  349. }
  350. } // namespace base