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 #ifndef BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
6 #define BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
7
8 #include "base/synchronization/atomic_flag.h"
9 #include "base/task/task_runner.h"
10 #include "base/task/thread_pool/task_tracker.h"
11 #include "base/task/thread_pool/test_utils.h"
12 #include "base/test/bind.h"
13 #include "base/test/test_timeouts.h"
14 #include "base/test/test_waitable_event.h"
15 #include "base/threading/platform_thread.h"
16 #include "build/build_config.h"
17
18 namespace base {
19 namespace internal {
20 namespace test {
21
22 // Verify that tasks only run when allowed by the CanRunPolicy. |target| is the
23 // object on which DidUpdateCanRunPolicy() must be called after updating the
24 // CanRunPolicy in |task_tracker|. |create_task_runner| is a function that
25 // receives a TaskPriority and returns a TaskRunner. |task_tracker| is the
26 // TaskTracker.
27 template <typename Target, typename CreateTaskRunner>
TestCanRunPolicyBasic(Target * target,CreateTaskRunner create_task_runner,TaskTracker * task_tracker)28 void TestCanRunPolicyBasic(Target* target,
29 CreateTaskRunner create_task_runner,
30 TaskTracker* task_tracker) {
31 AtomicFlag foreground_can_run;
32 TestWaitableEvent foreground_did_run;
33 AtomicFlag best_effort_can_run;
34 TestWaitableEvent best_effort_did_run;
35
36 task_tracker->SetCanRunPolicy(CanRunPolicy::kNone);
37 target->DidUpdateCanRunPolicy();
38
39 const auto user_visible_task_runner =
40 create_task_runner(TaskPriority::USER_VISIBLE);
41 user_visible_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
42 EXPECT_TRUE(foreground_can_run.IsSet());
43 foreground_did_run.Signal();
44 }));
45 const auto best_effort_task_runner =
46 create_task_runner(TaskPriority::BEST_EFFORT);
47 best_effort_task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
48 EXPECT_TRUE(best_effort_can_run.IsSet());
49 best_effort_did_run.Signal();
50 }));
51
52 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
53
54 foreground_can_run.Set();
55 task_tracker->SetCanRunPolicy(CanRunPolicy::kForegroundOnly);
56 target->DidUpdateCanRunPolicy();
57 foreground_did_run.Wait();
58
59 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
60
61 best_effort_can_run.Set();
62 task_tracker->SetCanRunPolicy(CanRunPolicy::kAll);
63 target->DidUpdateCanRunPolicy();
64 best_effort_did_run.Wait();
65 }
66
67 // Verify that if a task was allowed to run by the CanRunPolicy when it was
68 // posted, but the CanRunPolicy is updated to disallow it from running before it
69 // starts running, it doesn't run. |target| is the object on which
70 // DidUpdateCanRunPolicy() must be called after updating the CanRunPolicy in
71 // |task_tracker|. |create_task_runner| is a function that receives a
72 // TaskPriority and returns a *Sequenced*TaskRunner. |task_tracker| is the
73 // TaskTracker.
74 template <typename Target, typename CreateTaskRunner>
TestCanRunPolicyChangedBeforeRun(Target * target,CreateTaskRunner create_task_runner,TaskTracker * task_tracker)75 void TestCanRunPolicyChangedBeforeRun(Target* target,
76 CreateTaskRunner create_task_runner,
77 TaskTracker* task_tracker) {
78 constexpr struct {
79 // Descriptor for the test case.
80 const char* descriptor;
81 // Task priority being tested.
82 TaskPriority priority;
83 // Policy that disallows running tasks with |priority|.
84 CanRunPolicy disallow_policy;
85 // Policy that allows running tasks with |priority|.
86 CanRunPolicy allow_policy;
87 } kTestCases[] = {
88 {"BestEffort/kNone/kAll", TaskPriority::BEST_EFFORT, CanRunPolicy::kNone,
89 CanRunPolicy::kAll},
90 {"BestEffort/kForegroundOnly/kAll", TaskPriority::BEST_EFFORT,
91 CanRunPolicy::kForegroundOnly, CanRunPolicy::kAll},
92 {"UserVisible/kNone/kForegroundOnly", TaskPriority::USER_VISIBLE,
93 CanRunPolicy::kNone, CanRunPolicy::kForegroundOnly},
94 {"UserVisible/kNone/kAll", TaskPriority::USER_VISIBLE,
95 CanRunPolicy::kNone, CanRunPolicy::kAll}};
96
97 for (auto& test_case : kTestCases) {
98 SCOPED_TRACE(test_case.descriptor);
99
100 TestWaitableEvent first_task_started;
101 TestWaitableEvent first_task_blocked;
102 AtomicFlag second_task_can_run;
103
104 task_tracker->SetCanRunPolicy(test_case.allow_policy);
105 target->DidUpdateCanRunPolicy();
106
107 const auto task_runner = create_task_runner(test_case.priority);
108 task_runner->PostTask(
109 FROM_HERE, BindLambdaForTesting([&]() {
110 first_task_started.Signal();
111 first_task_blocked.Wait();
112 }));
113 task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
114 EXPECT_TRUE(second_task_can_run.IsSet());
115 }));
116
117 first_task_started.Wait();
118 task_tracker->SetCanRunPolicy(test_case.disallow_policy);
119 target->DidUpdateCanRunPolicy();
120 first_task_blocked.Signal();
121
122 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
123
124 second_task_can_run.Set();
125 task_tracker->SetCanRunPolicy(test_case.allow_policy);
126 target->DidUpdateCanRunPolicy();
127 task_tracker->FlushForTesting();
128 }
129 }
130
131 // Regression test for https://crbug.com/950383
132 template <typename Target, typename CreateTaskRunner>
TestCanRunPolicyLoad(Target * target,CreateTaskRunner create_task_runner,TaskTracker * task_tracker)133 void TestCanRunPolicyLoad(Target* target,
134 CreateTaskRunner create_task_runner,
135 TaskTracker* task_tracker) {
136 constexpr struct {
137 // Descriptor for the test case.
138 const char* descriptor;
139 // Task priority being tested.
140 TaskPriority priority;
141 // Policy that allows running tasks with |priority|.
142 CanRunPolicy allow_policy;
143 // Policy that disallows running tasks with |priority|.
144 CanRunPolicy disallow_policy;
145 } kTestCases[] = {
146 {"BestEffort/kAll/kNone", TaskPriority::BEST_EFFORT, CanRunPolicy::kAll,
147 CanRunPolicy::kNone},
148 {"BestEffort/kAll/kForegroundOnly", TaskPriority::BEST_EFFORT,
149 CanRunPolicy::kAll, CanRunPolicy::kForegroundOnly},
150 {"UserVisible/kForegroundOnly/kNone", TaskPriority::USER_VISIBLE,
151 CanRunPolicy::kForegroundOnly, CanRunPolicy::kNone},
152 {"UserVisible/kAll/kNone", TaskPriority::USER_VISIBLE, CanRunPolicy::kAll,
153 CanRunPolicy::kNone}};
154
155 for (auto& test_case : kTestCases) {
156 SCOPED_TRACE(test_case.descriptor);
157
158 task_tracker->SetCanRunPolicy(test_case.allow_policy);
159 target->DidUpdateCanRunPolicy();
160
161 const auto task_runner = create_task_runner(test_case.priority);
162
163 // Post less tasks on iOS to avoid timeouts.
164 const size_t kLargeNumber =
165 #if BUILDFLAG(IS_IOS)
166 16;
167 #else
168 256;
169 #endif
170 for (size_t i = 0; i < kLargeNumber; ++i)
171 task_runner->PostTask(FROM_HERE, DoNothing());
172
173 // Change the CanRunPolicy concurrently with running tasks.
174 // This should not cause crashes.
175 for (size_t i = 0; i < kLargeNumber; ++i) {
176 task_tracker->SetCanRunPolicy(test_case.disallow_policy);
177 target->DidUpdateCanRunPolicy();
178
179 task_tracker->SetCanRunPolicy(test_case.allow_policy);
180 target->DidUpdateCanRunPolicy();
181 }
182
183 task_tracker->FlushForTesting();
184 }
185 }
186
187 } // namespace test
188 } // namespace internal
189 } // namespace base
190
191 #endif // BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
192