tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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