xref: /aosp_15_r20/external/cronet/base/threading/platform_thread_win_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 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 <windows.h>
8 
9 #include <array>
10 
11 #include "base/process/process.h"
12 #include "base/test/scoped_feature_list.h"
13 #include "base/threading/platform_thread_win.h"
14 #include "base/threading/simple_thread.h"
15 #include "base/threading/threading_features.h"
16 #include "base/win/windows_version.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace base {
21 
22 // It has been observed that calling
23 // :SetThreadPriority(THREAD_MODE_BACKGROUND_BEGIN) in an IDLE_PRIORITY_CLASS
24 // process never affects the return value of ::GetThreadPriority() or
25 // the base priority reported in Process Explorer. It does however
26 // set the memory and I/O priorities to very low. This test confirms that
27 // behavior which we suspect is a Windows kernel bug. If this test starts
28 // failing, the mitigation for https://crbug.com/901483 in
29 // PlatformThread::SetCurrentThreadType() should be revisited.
TEST(PlatformThreadWinTest,SetBackgroundThreadModeFailsInIdlePriorityProcess)30 TEST(PlatformThreadWinTest, SetBackgroundThreadModeFailsInIdlePriorityProcess) {
31   PlatformThreadHandle::Handle thread_handle =
32       PlatformThread::CurrentHandle().platform_handle();
33 
34   // ::GetThreadPriority() is NORMAL. Memory priority is NORMAL.
35   // Note: There is no practical way to verify the I/O priority.
36   EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
37   internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
38 
39   // Set the process priority to IDLE.
40   // Note: Do not use Process::SetPriority() because it uses
41   // PROCESS_MODE_BACKGROUND_BEGIN instead of IDLE_PRIORITY_CLASS when
42   // the target is the current process.
43   EXPECT_EQ(::GetPriorityClass(Process::Current().Handle()),
44             static_cast<DWORD>(NORMAL_PRIORITY_CLASS));
45   ::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS);
46   EXPECT_EQ(Process::Current().GetOSPriority(),
47             static_cast<int>(IDLE_PRIORITY_CLASS));
48 
49   // GetThreadPriority() stays NORMAL. Memory priority stays NORMAL.
50   EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
51   internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
52 
53   // Begin thread mode background.
54   EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_BEGIN));
55 
56   // On Win10+, GetThreadPriority() stays NORMAL and memory priority becomes
57   // VERY_LOW.
58   //
59   // Note: this documents the aforementioned kernel bug. Ideally this would
60   // *not* be the case.
61   const int priority_after_thread_mode_background_begin =
62       ::GetThreadPriority(thread_handle);
63   EXPECT_EQ(priority_after_thread_mode_background_begin,
64             THREAD_PRIORITY_NORMAL);
65   internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
66 
67   PlatformThread::Sleep(base::Seconds(1));
68 
69   // After 1 second, GetThreadPriority() and memory priority don't change (this
70   // refutes the hypothesis that it simply takes time before GetThreadPriority()
71   // is updated after entering thread mode background).
72   EXPECT_EQ(::GetThreadPriority(thread_handle),
73             priority_after_thread_mode_background_begin);
74   internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
75 
76   // Set the process priority to NORMAL.
77   ::SetPriorityClass(Process::Current().Handle(), NORMAL_PRIORITY_CLASS);
78 
79   // GetThreadPriority() and memory priority don't change when the process
80   // priority changes.
81   EXPECT_EQ(::GetThreadPriority(thread_handle),
82             priority_after_thread_mode_background_begin);
83   internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
84 
85   // End thread mode background.
86   //
87   // Note: at least "ending" the semi-enforced background mode works...
88   EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END));
89 
90   // GetThreadPriority() stays/becomes NORMAL. Memory priority becomes NORMAL.
91   EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
92   internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
93 }
94 
95 namespace {
96 class MemoryPriorityAssertingThreadDelegate
97     : public base::PlatformThread::Delegate {
98  public:
MemoryPriorityAssertingThreadDelegate(LONG memory_priority)99   explicit MemoryPriorityAssertingThreadDelegate(LONG memory_priority)
100       : memory_priority_(memory_priority) {}
101 
ThreadMain()102   void ThreadMain() override {
103     PlatformThreadHandle::Handle thread_handle =
104         PlatformThread::CurrentHandle().platform_handle();
105     internal::AssertMemoryPriority(thread_handle, memory_priority_);
106   }
107 
108   LONG memory_priority_;
109 };
110 }  // namespace
111 
112 // It has been observed (crbug.com/1489467) that memory priority is set to very
113 // low on background threads, and a possible mitigation is running in the
114 // kThreadNormalMemoryPriorityWin experiment which sets memory priority to
115 // NORMAL on all threads at creation. If this test fails, the feature is broken
116 // and investigation needs to be done into whether pages are being allocated at
117 // pri-1 despite it as shown in the above linked bug.
TEST(PlatformThreadWinTest,NormalPriorityFeatureForBackgroundThreads)118 TEST(PlatformThreadWinTest, NormalPriorityFeatureForBackgroundThreads) {
119   base::test::ScopedFeatureList list;
120   list.InitAndEnableFeature(kBackgroundThreadNormalMemoryPriorityWin);
121   base::InitializePlatformThreadFeatures();
122 
123   MemoryPriorityAssertingThreadDelegate delegate{MEMORY_PRIORITY_NORMAL};
124 
125   PlatformThreadHandle handle;
126 
127   CHECK(PlatformThread::CreateWithType(0, &delegate, &handle,
128                                        ThreadType::kBackground));
129   PlatformThread::Join(handle);
130 }
131 
TEST(PlatformThreadWinTest,BackgroundThreadsSetLowMemoryPriority)132 TEST(PlatformThreadWinTest, BackgroundThreadsSetLowMemoryPriority) {
133   base::InitializePlatformThreadFeatures();
134 
135   MemoryPriorityAssertingThreadDelegate delegate{MEMORY_PRIORITY_VERY_LOW};
136 
137   PlatformThreadHandle handle;
138 
139   CHECK(PlatformThread::CreateWithType(0, &delegate, &handle,
140                                        ThreadType::kBackground));
141   PlatformThread::Join(handle);
142 }
143 
144 }  // namespace base
145