1 // Copyright 2019 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/scoped_thread_priority.h"
6
7 #include "base/test/gtest_util.h"
8 #include "base/threading/platform_thread.h"
9 #include "base/threading/thread.h"
10 #include "build/build_config.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace base {
14
15 namespace {
16
17 // Tests in this file invoke an API that tracks state in static variable. They
18 // can therefore only be invoked once per process.
19 #define ASSERT_RUNS_ONCE() \
20 static int num_times_run = 0; \
21 ++num_times_run; \
22 if (num_times_run > 1) \
23 ADD_FAILURE() << "This test cannot run multiple times in the same " \
24 "process.";
25
26 static ThreadType kAllThreadTypes[] = {
27 ThreadType::kRealtimeAudio, ThreadType::kDisplayCritical,
28 ThreadType::kDefault, ThreadType::kBackground};
29
30 static_assert(static_cast<int>(ThreadType::kBackground) == 0,
31 "kBackground isn't lowest");
32 static_assert(ThreadType::kRealtimeAudio == ThreadType::kMaxValue,
33 "kRealtimeAudio isn't highest");
34
35 class ScopedThreadPriorityTest : public testing::Test {
36 protected:
SetUp()37 void SetUp() override {
38 // Ensures the default thread priority is set.
39 PlatformThread::SetCurrentThreadType(ThreadType::kDefault);
40 ASSERT_EQ(ThreadPriorityForTest::kNormal,
41 PlatformThread::GetCurrentThreadPriorityForTest());
42 }
43 };
44
45 using ScopedThreadPriorityDeathTest = ScopedThreadPriorityTest;
46
47 #if BUILDFLAG(IS_WIN)
FunctionThatBoostsPriorityOnFirstInvoke(ThreadPriorityForTest expected_priority)48 void FunctionThatBoostsPriorityOnFirstInvoke(
49 ThreadPriorityForTest expected_priority) {
50 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
51 EXPECT_EQ(expected_priority,
52 PlatformThread::GetCurrentThreadPriorityForTest());
53 }
54
FunctionThatBoostsPriorityOnEveryInvoke()55 void FunctionThatBoostsPriorityOnEveryInvoke() {
56 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
57 EXPECT_EQ(base::ThreadPriorityForTest::kNormal,
58 PlatformThread::GetCurrentThreadPriorityForTest());
59 }
60
61 #endif // BUILDFLAG(IS_WIN)
62
63 } // namespace
64
TEST_F(ScopedThreadPriorityTest,BasicTest)65 TEST_F(ScopedThreadPriorityTest, BasicTest) {
66 for (auto from : kAllThreadTypes) {
67 if (!PlatformThread::CanChangeThreadType(ThreadType::kDefault, from))
68 continue;
69 for (auto to : kAllThreadTypes) {
70 // ThreadType::kRealtimeAudio is not a valid |target_thread_type| for
71 // ScopedBoostPriority.
72 if (to == ThreadType::kRealtimeAudio)
73 continue;
74 Thread thread("ScopedThreadPriorityTest");
75 thread.StartWithOptions(Thread::Options(from));
76 thread.WaitUntilThreadStarted();
77 thread.task_runner()->PostTask(
78 FROM_HERE,
79 BindOnce(
80 [](ThreadType from, ThreadType to) {
81 EXPECT_EQ(PlatformThread::GetCurrentThreadType(), from);
82 {
83 ScopedBoostPriority scoped_boost_priority(to);
84 bool will_boost_priority =
85 from < to &&
86 PlatformThread::CanChangeThreadType(from, to) &&
87 PlatformThread::CanChangeThreadType(to, from);
88 EXPECT_EQ(PlatformThread::GetCurrentThreadType(),
89 will_boost_priority ? to : from);
90 }
91 EXPECT_EQ(PlatformThread::GetCurrentThreadType(), from);
92 },
93 from, to));
94 }
95 }
96 }
97
TEST_F(ScopedThreadPriorityDeathTest,NoRealTime)98 TEST_F(ScopedThreadPriorityDeathTest, NoRealTime) {
99 EXPECT_DCHECK_DEATH({
100 ScopedBoostPriority scoped_boost_priority(ThreadType::kRealtimeAudio);
101 });
102 }
103
TEST_F(ScopedThreadPriorityTest,WithoutPriorityBoost)104 TEST_F(ScopedThreadPriorityTest, WithoutPriorityBoost) {
105 ASSERT_RUNS_ONCE();
106
107 // Validates that a thread at normal priority keep the same priority.
108 {
109 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
110 EXPECT_EQ(ThreadPriorityForTest::kNormal,
111 PlatformThread::GetCurrentThreadPriorityForTest());
112 }
113 EXPECT_EQ(ThreadPriorityForTest::kNormal,
114 PlatformThread::GetCurrentThreadPriorityForTest());
115 }
116
117 #if BUILDFLAG(IS_WIN)
TEST_F(ScopedThreadPriorityTest,WithPriorityBoost)118 TEST_F(ScopedThreadPriorityTest, WithPriorityBoost) {
119 ASSERT_RUNS_ONCE();
120
121 // Validates that a thread at background priority is boosted to normal
122 // priority.
123 PlatformThread::SetCurrentThreadType(ThreadType::kBackground);
124 {
125 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
126 EXPECT_EQ(ThreadPriorityForTest::kNormal,
127 PlatformThread::GetCurrentThreadPriorityForTest());
128 }
129 EXPECT_EQ(ThreadPriorityForTest::kBackground,
130 PlatformThread::GetCurrentThreadPriorityForTest());
131
132 // Put back the default thread priority.
133 PlatformThread::SetCurrentThreadType(ThreadType::kDefault);
134 }
135 #endif // BUILDFLAG(IS_WIN)
136
137 #if BUILDFLAG(IS_WIN)
TEST_F(ScopedThreadPriorityTest,NestedScope)138 TEST_F(ScopedThreadPriorityTest, NestedScope) {
139 ASSERT_RUNS_ONCE();
140
141 PlatformThread::SetCurrentThreadType(ThreadType::kBackground);
142
143 {
144 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
145 EXPECT_EQ(ThreadPriorityForTest::kNormal,
146 PlatformThread::GetCurrentThreadPriorityForTest());
147 {
148 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
149 EXPECT_EQ(ThreadPriorityForTest::kNormal,
150 PlatformThread::GetCurrentThreadPriorityForTest());
151 }
152 EXPECT_EQ(ThreadPriorityForTest::kNormal,
153 PlatformThread::GetCurrentThreadPriorityForTest());
154 }
155
156 EXPECT_EQ(ThreadPriorityForTest::kBackground,
157 PlatformThread::GetCurrentThreadPriorityForTest());
158
159 // Put back the default thread priority.
160 PlatformThread::SetCurrentThreadType(ThreadType::kDefault);
161 }
162 #endif // BUILDFLAG(IS_WIN)
163
164 #if BUILDFLAG(IS_WIN)
TEST_F(ScopedThreadPriorityTest,FunctionThatBoostsPriorityOnFirstInvoke)165 TEST_F(ScopedThreadPriorityTest, FunctionThatBoostsPriorityOnFirstInvoke) {
166 ASSERT_RUNS_ONCE();
167
168 PlatformThread::SetCurrentThreadType(ThreadType::kBackground);
169
170 FunctionThatBoostsPriorityOnFirstInvoke(base::ThreadPriorityForTest::kNormal);
171 FunctionThatBoostsPriorityOnFirstInvoke(
172 base::ThreadPriorityForTest::kBackground);
173
174 // Put back the default thread priority.
175 PlatformThread::SetCurrentThreadType(ThreadType::kDefault);
176 }
177
TEST_F(ScopedThreadPriorityTest,FunctionThatBoostsPriorityOnEveryInvoke)178 TEST_F(ScopedThreadPriorityTest, FunctionThatBoostsPriorityOnEveryInvoke) {
179 PlatformThread::SetCurrentThreadType(ThreadType::kBackground);
180
181 FunctionThatBoostsPriorityOnEveryInvoke();
182 FunctionThatBoostsPriorityOnEveryInvoke();
183
184 // Put back the default thread priority.
185 PlatformThread::SetCurrentThreadType(ThreadType::kDefault);
186 }
187
188 #endif // BUILDFLAG(IS_WIN)
189
190 } // namespace base
191