platform_thread_win.cc (22442B)
1 // Copyright 2012 The Chromium Authors 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_win.h" 6 7 #include <stddef.h> 8 9 #include <string> 10 11 #include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" 12 #include "base/debug/alias.h" 13 #include "base/debug/crash_logging.h" 14 #include "base/debug/profiler.h" 15 #include "base/feature_list.h" 16 #include "base/logging.h" 17 #include "base/memory/raw_ptr.h" 18 #include "base/metrics/histogram_macros.h" 19 #include "base/process/memory.h" 20 #include "base/strings/string_number_conversions.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "base/threading/scoped_blocking_call.h" 23 #include "base/threading/scoped_thread_priority.h" 24 #include "base/threading/thread_id_name_manager.h" 25 #include "base/threading/thread_restrictions.h" 26 #include "base/threading/threading_features.h" 27 #include "base/time/time_override.h" 28 #include "base/win/scoped_handle.h" 29 #include "base/win/windows_version.h" 30 #include "build/build_config.h" 31 32 #include <windows.h> 33 34 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) 35 #include "base/allocator/partition_allocator/src/partition_alloc/starscan/pcscan.h" 36 #include "base/allocator/partition_allocator/src/partition_alloc/starscan/stack/stack.h" 37 #endif 38 39 namespace base { 40 41 BASE_FEATURE(kUseThreadPriorityLowest, 42 "UseThreadPriorityLowest", 43 base::FEATURE_DISABLED_BY_DEFAULT); 44 BASE_FEATURE(kAboveNormalCompositingBrowserWin, 45 "AboveNormalCompositingBrowserWin", 46 base::FEATURE_ENABLED_BY_DEFAULT); 47 48 BASE_FEATURE(kBackgroundThreadNormalMemoryPriorityWin, 49 "BackgroundThreadNormalMemoryPriorityWin", 50 base::FEATURE_DISABLED_BY_DEFAULT); 51 52 namespace { 53 54 // Flag used to set thread priority to |THREAD_PRIORITY_LOWEST| for 55 // |kUseThreadPriorityLowest| Feature. 56 std::atomic<bool> g_use_thread_priority_lowest{false}; 57 // Flag used to map Compositing ThreadType |THREAD_PRIORITY_ABOVE_NORMAL| on the 58 // UI thread for |kAboveNormalCompositingBrowserWin| Feature. 59 std::atomic<bool> g_above_normal_compositing_browser{true}; 60 // Flag used to set thread memory priority to |MEMORY_PRIORITY_NORMAL| on 61 // background threads for |kThreadNormalMemoryPriorityWin| Feature. 62 std::atomic<bool> g_background_thread_normal_memory_priority_win{false}; 63 64 // These values are sometimes returned by ::GetThreadPriority(). 65 constexpr int kWinDisplayPriority1 = 5; 66 constexpr int kWinDisplayPriority2 = 6; 67 68 // The information on how to set the thread name comes from 69 // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx 70 const DWORD kVCThreadNameException = 0x406D1388; 71 72 typedef struct tagTHREADNAME_INFO { 73 DWORD dwType; // Must be 0x1000. 74 LPCSTR szName; // Pointer to name (in user addr space). 75 DWORD dwThreadID; // Thread ID (-1=caller thread). 76 DWORD dwFlags; // Reserved for future use, must be zero. 77 } THREADNAME_INFO; 78 79 // The SetThreadDescription API was brought in version 1607 of Windows 10. 80 typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, 81 PCWSTR lpThreadDescription); 82 83 // This function has try handling, so it is separated out of its caller. 84 void SetNameInternal(PlatformThreadId thread_id, const char* name) { 85 //This function is only used for debugging purposes, as you can find by its caller 86 #ifndef __MINGW32__ 87 THREADNAME_INFO info; 88 info.dwType = 0x1000; 89 info.szName = name; 90 info.dwThreadID = thread_id; 91 info.dwFlags = 0; 92 93 __try { 94 RaiseException(kVCThreadNameException, 0, sizeof(info) / sizeof(ULONG_PTR), 95 reinterpret_cast<ULONG_PTR*>(&info)); 96 } __except (EXCEPTION_EXECUTE_HANDLER) { 97 } 98 #endif 99 } 100 101 #if !defined(MOZ_SANDBOX) 102 struct ThreadParams { 103 raw_ptr<PlatformThread::Delegate> delegate; 104 bool joinable; 105 ThreadType thread_type; 106 MessagePumpType message_pump_type; 107 }; 108 109 DWORD __stdcall ThreadFunc(void* params) { 110 ThreadParams* thread_params = static_cast<ThreadParams*>(params); 111 PlatformThread::Delegate* delegate = thread_params->delegate; 112 if (!thread_params->joinable) 113 base::DisallowSingleton(); 114 115 if (thread_params->thread_type != ThreadType::kDefault) 116 internal::SetCurrentThreadType(thread_params->thread_type, 117 thread_params->message_pump_type); 118 119 // Retrieve a copy of the thread handle to use as the key in the 120 // thread name mapping. 121 PlatformThreadHandle::Handle platform_handle; 122 BOOL did_dup = DuplicateHandle(GetCurrentProcess(), 123 GetCurrentThread(), 124 GetCurrentProcess(), 125 &platform_handle, 126 0, 127 FALSE, 128 DUPLICATE_SAME_ACCESS); 129 130 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) 131 partition_alloc::internal::PCScan::NotifyThreadCreated( 132 partition_alloc::internal::GetStackPointer()); 133 #endif 134 135 win::ScopedHandle scoped_platform_handle; 136 137 if (did_dup) { 138 scoped_platform_handle.Set(platform_handle); 139 ThreadIdNameManager::GetInstance()->RegisterThread( 140 scoped_platform_handle.get(), PlatformThread::CurrentId()); 141 } 142 143 delete thread_params; 144 delegate->ThreadMain(); 145 146 if (did_dup) { 147 ThreadIdNameManager::GetInstance()->RemoveName(scoped_platform_handle.get(), 148 PlatformThread::CurrentId()); 149 } 150 151 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(USE_STARSCAN) 152 partition_alloc::internal::PCScan::NotifyThreadDestroyed(); 153 #endif 154 155 // Ensure thread priority is at least NORMAL before initiating thread 156 // destruction. Thread destruction on Windows holds the LdrLock while 157 // performing TLS destruction which causes hangs if performed at background 158 // priority (priority inversion) (see: http://crbug.com/1096203). 159 if (::GetThreadPriority(::GetCurrentThread()) < THREAD_PRIORITY_NORMAL) 160 PlatformThread::SetCurrentThreadType(ThreadType::kDefault); 161 162 return 0; 163 } 164 165 // CreateThreadInternal() matches PlatformThread::CreateWithType(), except 166 // that |out_thread_handle| may be nullptr, in which case a non-joinable thread 167 // is created. 168 bool CreateThreadInternal(size_t stack_size, 169 PlatformThread::Delegate* delegate, 170 PlatformThreadHandle* out_thread_handle, 171 ThreadType thread_type, 172 MessagePumpType message_pump_type) { 173 unsigned int flags = 0; 174 if (stack_size > 0) { 175 flags = STACK_SIZE_PARAM_IS_A_RESERVATION; 176 #if defined(ARCH_CPU_32_BITS) 177 } else { 178 // The process stack size is increased to give spaces to |RendererMain| in 179 // |chrome/BUILD.gn|, but keep the default stack size of other threads to 180 // 1MB for the address space pressure. 181 flags = STACK_SIZE_PARAM_IS_A_RESERVATION; 182 static BOOL is_wow64 = -1; 183 if (is_wow64 == -1 && !IsWow64Process(GetCurrentProcess(), &is_wow64)) 184 is_wow64 = FALSE; 185 // When is_wow64 is set that means we are running on 64-bit Windows and we 186 // get 4 GiB of address space. In that situation we can afford to use 1 MiB 187 // of address space for stacks. When running on 32-bit Windows we only get 188 // 2 GiB of address space so we need to conserve. Typically stack usage on 189 // these threads is only about 100 KiB. 190 if (is_wow64) 191 stack_size = 1024 * 1024; 192 else 193 stack_size = 512 * 1024; 194 #endif 195 } 196 197 ThreadParams* params = new ThreadParams; 198 params->delegate = delegate; 199 params->joinable = out_thread_handle != nullptr; 200 params->thread_type = thread_type; 201 params->message_pump_type = message_pump_type; 202 203 // Using CreateThread here vs _beginthreadex makes thread creation a bit 204 // faster and doesn't require the loader lock to be available. Our code will 205 // have to work running on CreateThread() threads anyway, since we run code on 206 // the Windows thread pool, etc. For some background on the difference: 207 // http://www.microsoft.com/msj/1099/win32/win321099.aspx 208 void* thread_handle = 209 ::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr); 210 211 if (!thread_handle) { 212 DWORD last_error = ::GetLastError(); 213 214 switch (last_error) { 215 case ERROR_NOT_ENOUGH_MEMORY: 216 case ERROR_OUTOFMEMORY: 217 case ERROR_COMMITMENT_LIMIT: 218 TerminateBecauseOutOfMemory(stack_size); 219 break; 220 221 default: 222 static auto* last_error_crash_key = debug::AllocateCrashKeyString( 223 "create_thread_last_error", debug::CrashKeySize::Size32); 224 debug::SetCrashKeyString(last_error_crash_key, 225 base::NumberToString(last_error)); 226 break; 227 } 228 229 delete params; 230 return false; 231 } 232 233 if (out_thread_handle) 234 *out_thread_handle = PlatformThreadHandle(thread_handle); 235 else 236 CloseHandle(thread_handle); 237 return true; 238 } 239 #endif 240 241 } // namespace 242 243 namespace internal { 244 245 void AssertMemoryPriority(HANDLE thread, int memory_priority) { 246 #if DCHECK_IS_ON() 247 static const auto get_thread_information_fn = 248 reinterpret_cast<decltype(&::GetThreadInformation)>(::GetProcAddress( 249 ::GetModuleHandle(L"Kernel32.dll"), "GetThreadInformation")); 250 251 DCHECK(get_thread_information_fn); 252 253 MEMORY_PRIORITY_INFORMATION memory_priority_information = {}; 254 DCHECK(get_thread_information_fn(thread, ::ThreadMemoryPriority, 255 &memory_priority_information, 256 sizeof(memory_priority_information))); 257 258 DCHECK_EQ(memory_priority, 259 static_cast<int>(memory_priority_information.MemoryPriority)); 260 #endif 261 } 262 263 } // namespace internal 264 265 // static 266 PlatformThreadId PlatformThread::CurrentId() { 267 return ::GetCurrentThreadId(); 268 } 269 270 // static 271 PlatformThreadRef PlatformThread::CurrentRef() { 272 return PlatformThreadRef(::GetCurrentThreadId()); 273 } 274 275 // static 276 PlatformThreadHandle PlatformThread::CurrentHandle() { 277 return PlatformThreadHandle(::GetCurrentThread()); 278 } 279 280 // static 281 void PlatformThread::YieldCurrentThread() { 282 ::Sleep(0); 283 } 284 285 // static 286 void PlatformThread::Sleep(TimeDelta duration) { 287 // When measured with a high resolution clock, Sleep() sometimes returns much 288 // too early. We may need to call it repeatedly to get the desired duration. 289 // PlatformThread::Sleep doesn't support mock-time, so this always uses 290 // real-time. 291 const TimeTicks end = subtle::TimeTicksNowIgnoringOverride() + duration; 292 for (TimeTicks now = subtle::TimeTicksNowIgnoringOverride(); now < end; 293 now = subtle::TimeTicksNowIgnoringOverride()) { 294 ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp())); 295 } 296 } 297 298 // static 299 void PlatformThread::SetName(const std::string& name) { 300 SetNameCommon(name); 301 302 // The SetThreadDescription API works even if no debugger is attached. 303 static auto set_thread_description_func = 304 reinterpret_cast<SetThreadDescription>(::GetProcAddress( 305 ::GetModuleHandle(L"Kernel32.dll"), "SetThreadDescription")); 306 if (set_thread_description_func) { 307 set_thread_description_func(::GetCurrentThread(), 308 base::UTF8ToWide(name).c_str()); 309 } 310 311 // The debugger needs to be around to catch the name in the exception. If 312 // there isn't a debugger, we are just needlessly throwing an exception. 313 if (!::IsDebuggerPresent()) 314 return; 315 316 SetNameInternal(CurrentId(), name.c_str()); 317 } 318 319 // static 320 const char* PlatformThread::GetName() { 321 return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); 322 } 323 324 #if !defined(MOZ_SANDBOX) 325 // static 326 bool PlatformThread::CreateWithType(size_t stack_size, 327 Delegate* delegate, 328 PlatformThreadHandle* thread_handle, 329 ThreadType thread_type, 330 MessagePumpType pump_type_hint) { 331 DCHECK(thread_handle); 332 return CreateThreadInternal(stack_size, delegate, thread_handle, thread_type, 333 pump_type_hint); 334 } 335 336 // static 337 bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { 338 return CreateNonJoinableWithType(stack_size, delegate, ThreadType::kDefault); 339 } 340 341 // static 342 bool PlatformThread::CreateNonJoinableWithType(size_t stack_size, 343 Delegate* delegate, 344 ThreadType thread_type, 345 MessagePumpType pump_type_hint) { 346 return CreateThreadInternal(stack_size, delegate, nullptr /* non-joinable */, 347 thread_type, pump_type_hint); 348 } 349 #endif 350 351 // static 352 void PlatformThread::Join(PlatformThreadHandle thread_handle) { 353 DCHECK(thread_handle.platform_handle()); 354 355 DWORD thread_id = 0; 356 thread_id = ::GetThreadId(thread_handle.platform_handle()); 357 DWORD last_error = 0; 358 if (!thread_id) 359 last_error = ::GetLastError(); 360 361 // Record information about the exiting thread in case joining hangs. 362 base::debug::Alias(&thread_id); 363 base::debug::Alias(&last_error); 364 365 base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( 366 FROM_HERE, base::BlockingType::MAY_BLOCK); 367 368 // Wait for the thread to exit. It should already have terminated but make 369 // sure this assumption is valid. 370 CHECK_EQ(WAIT_OBJECT_0, 371 WaitForSingleObject(thread_handle.platform_handle(), INFINITE)); 372 CloseHandle(thread_handle.platform_handle()); 373 } 374 375 // static 376 void PlatformThread::Detach(PlatformThreadHandle thread_handle) { 377 CloseHandle(thread_handle.platform_handle()); 378 } 379 380 // static 381 bool PlatformThread::CanChangeThreadType(ThreadType from, ThreadType to) { 382 return true; 383 } 384 385 #if !defined(MOZ_SANDBOX) 386 namespace { 387 388 void SetCurrentThreadPriority(ThreadType thread_type, 389 MessagePumpType pump_type_hint) { 390 if (thread_type == ThreadType::kCompositing && 391 pump_type_hint == MessagePumpType::UI && 392 !g_above_normal_compositing_browser) { 393 // Ignore kCompositing thread type for UI thread as Windows has a 394 // priority boost mechanism. See 395 // https://docs.microsoft.com/en-us/windows/win32/procthread/priority-boosts 396 return; 397 } 398 399 PlatformThreadHandle::Handle thread_handle = 400 PlatformThread::CurrentHandle().platform_handle(); 401 402 if (!g_use_thread_priority_lowest && thread_type != ThreadType::kBackground) { 403 // Exit background mode if the new priority is not BACKGROUND. This is a 404 // no-op if not in background mode. 405 ::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END); 406 // We used to DCHECK that memory priority is MEMORY_PRIORITY_NORMAL here, 407 // but found that it is not always the case (e.g. in the installer). 408 // crbug.com/1340578#c2 409 } 410 411 int desired_priority = THREAD_PRIORITY_ERROR_RETURN; 412 switch (thread_type) { 413 case ThreadType::kBackground: 414 // Using THREAD_MODE_BACKGROUND_BEGIN instead of THREAD_PRIORITY_LOWEST 415 // improves input latency and navigation time. See 416 // https://docs.google.com/document/d/16XrOwuwTwKWdgPbcKKajTmNqtB4Am8TgS9GjbzBYLc0 417 // 418 // MSDN recommends THREAD_MODE_BACKGROUND_BEGIN for threads that perform 419 // background work, as it reduces disk and memory priority in addition to 420 // CPU priority. 421 desired_priority = 422 g_use_thread_priority_lowest.load(std::memory_order_relaxed) 423 ? THREAD_PRIORITY_LOWEST 424 : THREAD_MODE_BACKGROUND_BEGIN; 425 break; 426 case ThreadType::kUtility: 427 desired_priority = THREAD_PRIORITY_BELOW_NORMAL; 428 break; 429 case ThreadType::kResourceEfficient: 430 case ThreadType::kDefault: 431 desired_priority = THREAD_PRIORITY_NORMAL; 432 break; 433 case ThreadType::kCompositing: 434 case ThreadType::kDisplayCritical: 435 desired_priority = THREAD_PRIORITY_ABOVE_NORMAL; 436 break; 437 case ThreadType::kRealtimeAudio: 438 desired_priority = THREAD_PRIORITY_TIME_CRITICAL; 439 break; 440 } 441 DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN); 442 443 [[maybe_unused]] const BOOL cpu_priority_success = 444 ::SetThreadPriority(thread_handle, desired_priority); 445 DPLOG_IF(ERROR, !cpu_priority_success) 446 << "Failed to set thread priority to " << desired_priority; 447 448 if (g_background_thread_normal_memory_priority_win && 449 desired_priority == THREAD_MODE_BACKGROUND_BEGIN) { 450 // Override the memory priority. 451 MEMORY_PRIORITY_INFORMATION memory_priority{.MemoryPriority = 452 MEMORY_PRIORITY_NORMAL}; 453 [[maybe_unused]] const BOOL memory_priority_success = 454 SetThreadInformation(thread_handle, ::ThreadMemoryPriority, 455 &memory_priority, sizeof(memory_priority)); 456 DPLOG_IF(ERROR, !memory_priority_success) 457 << "Set thread memory priority failed."; 458 } 459 460 if (!g_use_thread_priority_lowest && thread_type == ThreadType::kBackground) { 461 // In a background process, THREAD_MODE_BACKGROUND_BEGIN lowers the memory 462 // and I/O priorities but not the CPU priority (kernel bug?). Use 463 // THREAD_PRIORITY_LOWEST to also lower the CPU priority. 464 // https://crbug.com/901483 465 if (PlatformThread::GetCurrentThreadPriorityForTest() != 466 ThreadPriorityForTest::kBackground) { 467 ::SetThreadPriority(thread_handle, THREAD_PRIORITY_LOWEST); 468 // We used to DCHECK that memory priority is MEMORY_PRIORITY_VERY_LOW 469 // here, but found that it is not always the case (e.g. in the installer). 470 // crbug.com/1340578#c2 471 } 472 } 473 } 474 475 void SetCurrentThreadQualityOfService(ThreadType thread_type) { 476 // QoS and power throttling were introduced in Win10 1709. 477 bool desire_ecoqos = false; 478 switch (thread_type) { 479 case ThreadType::kBackground: 480 case ThreadType::kUtility: 481 case ThreadType::kResourceEfficient: 482 desire_ecoqos = true; 483 break; 484 case ThreadType::kDefault: 485 case ThreadType::kCompositing: 486 case ThreadType::kDisplayCritical: 487 case ThreadType::kRealtimeAudio: 488 desire_ecoqos = false; 489 break; 490 } 491 492 THREAD_POWER_THROTTLING_STATE thread_power_throttling_state{ 493 .Version = THREAD_POWER_THROTTLING_CURRENT_VERSION, 494 .ControlMask = 495 desire_ecoqos ? THREAD_POWER_THROTTLING_EXECUTION_SPEED : 0ul, 496 .StateMask = 497 desire_ecoqos ? THREAD_POWER_THROTTLING_EXECUTION_SPEED : 0ul, 498 }; 499 [[maybe_unused]] const BOOL success = ::SetThreadInformation( 500 ::GetCurrentThread(), ::ThreadPowerThrottling, 501 &thread_power_throttling_state, sizeof(thread_power_throttling_state)); 502 // Failure is expected on versions of Windows prior to RS3. 503 DPLOG_IF(ERROR, !success && win::GetVersion() >= win::Version::WIN10_RS3) 504 << "Failed to set EcoQoS to " << std::boolalpha << desire_ecoqos; 505 } 506 507 } // namespace 508 509 namespace internal { 510 511 void SetCurrentThreadTypeImpl(ThreadType thread_type, 512 MessagePumpType pump_type_hint) { 513 SetCurrentThreadPriority(thread_type, pump_type_hint); 514 SetCurrentThreadQualityOfService(thread_type); 515 } 516 517 } // namespace internal 518 #endif 519 520 // static 521 ThreadPriorityForTest PlatformThread::GetCurrentThreadPriorityForTest() { 522 static_assert( 523 THREAD_PRIORITY_IDLE < 0, 524 "THREAD_PRIORITY_IDLE is >= 0 and will incorrectly cause errors."); 525 static_assert( 526 THREAD_PRIORITY_LOWEST < 0, 527 "THREAD_PRIORITY_LOWEST is >= 0 and will incorrectly cause errors."); 528 static_assert(THREAD_PRIORITY_BELOW_NORMAL < 0, 529 "THREAD_PRIORITY_BELOW_NORMAL is >= 0 and will incorrectly " 530 "cause errors."); 531 static_assert( 532 THREAD_PRIORITY_NORMAL == 0, 533 "The logic below assumes that THREAD_PRIORITY_NORMAL is zero. If it is " 534 "not, ThreadPriorityForTest::kBackground may be incorrectly detected."); 535 static_assert(THREAD_PRIORITY_ABOVE_NORMAL >= 0, 536 "THREAD_PRIORITY_ABOVE_NORMAL is < 0 and will incorrectly be " 537 "translated to ThreadPriorityForTest::kBackground."); 538 static_assert(THREAD_PRIORITY_HIGHEST >= 0, 539 "THREAD_PRIORITY_HIGHEST is < 0 and will incorrectly be " 540 "translated to ThreadPriorityForTest::kBackground."); 541 static_assert(THREAD_PRIORITY_TIME_CRITICAL >= 0, 542 "THREAD_PRIORITY_TIME_CRITICAL is < 0 and will incorrectly be " 543 "translated to ThreadPriorityForTest::kBackground."); 544 static_assert(THREAD_PRIORITY_ERROR_RETURN >= 0, 545 "THREAD_PRIORITY_ERROR_RETURN is < 0 and will incorrectly be " 546 "translated to ThreadPriorityForTest::kBackground."); 547 548 const int priority = 549 ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle()); 550 551 // Negative values represent a background priority. We have observed -3, -4, 552 // -6 when THREAD_MODE_BACKGROUND_* is used. THREAD_PRIORITY_IDLE, 553 // THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_BELOW_NORMAL are other possible 554 // negative values. 555 if (priority < THREAD_PRIORITY_BELOW_NORMAL) 556 return ThreadPriorityForTest::kBackground; 557 558 switch (priority) { 559 case THREAD_PRIORITY_BELOW_NORMAL: 560 return ThreadPriorityForTest::kUtility; 561 case THREAD_PRIORITY_NORMAL: 562 return ThreadPriorityForTest::kNormal; 563 case kWinDisplayPriority1: 564 [[fallthrough]]; 565 case kWinDisplayPriority2: 566 return ThreadPriorityForTest::kDisplay; 567 case THREAD_PRIORITY_ABOVE_NORMAL: 568 case THREAD_PRIORITY_HIGHEST: 569 return ThreadPriorityForTest::kDisplay; 570 case THREAD_PRIORITY_TIME_CRITICAL: 571 return ThreadPriorityForTest::kRealtimeAudio; 572 case THREAD_PRIORITY_ERROR_RETURN: 573 DPCHECK(false) << "::GetThreadPriority error"; 574 } 575 576 NOTREACHED() << "::GetThreadPriority returned " << priority << "."; 577 return ThreadPriorityForTest::kNormal; 578 } 579 580 void InitializePlatformThreadFeatures() { 581 g_use_thread_priority_lowest.store( 582 FeatureList::IsEnabled(kUseThreadPriorityLowest), 583 std::memory_order_relaxed); 584 g_above_normal_compositing_browser.store( 585 FeatureList::IsEnabled(kAboveNormalCompositingBrowserWin), 586 std::memory_order_relaxed); 587 g_background_thread_normal_memory_priority_win.store( 588 FeatureList::IsEnabled(kBackgroundThreadNormalMemoryPriorityWin), 589 std::memory_order_relaxed); 590 } 591 592 // static 593 size_t PlatformThread::GetDefaultThreadStackSize() { 594 return 0; 595 } 596 597 } // namespace base