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