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(FROM_HERE, BindLambdaForTesting([&] {
109 first_task_started.Signal();
110 first_task_blocked.Wait();
111 }));
112 task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&] {
113 EXPECT_TRUE(second_task_can_run.IsSet());
114 }));
115
116 first_task_started.Wait();
117 task_tracker->SetCanRunPolicy(test_case.disallow_policy);
118 target->DidUpdateCanRunPolicy();
119 first_task_blocked.Signal();
120
121 PlatformThread::Sleep(TestTimeouts::tiny_timeout());
122
123 second_task_can_run.Set();
124 task_tracker->SetCanRunPolicy(test_case.allow_policy);
125 target->DidUpdateCanRunPolicy();
126 task_tracker->FlushForTesting();
127 }
128 }
129
130 // Regression test for https://crbug.com/950383
131 template <typename Target, typename CreateTaskRunner>
TestCanRunPolicyLoad(Target * target,CreateTaskRunner create_task_runner,TaskTracker * task_tracker)132 void TestCanRunPolicyLoad(Target* target,
133 CreateTaskRunner create_task_runner,
134 TaskTracker* task_tracker) {
135 constexpr struct {
136 // Descriptor for the test case.
137 const char* descriptor;
138 // Task priority being tested.
139 TaskPriority priority;
140 // Policy that allows running tasks with |priority|.
141 CanRunPolicy allow_policy;
142 // Policy that disallows running tasks with |priority|.
143 CanRunPolicy disallow_policy;
144 } kTestCases[] = {
145 {"BestEffort/kAll/kNone", TaskPriority::BEST_EFFORT, CanRunPolicy::kAll,
146 CanRunPolicy::kNone},
147 {"BestEffort/kAll/kForegroundOnly", TaskPriority::BEST_EFFORT,
148 CanRunPolicy::kAll, CanRunPolicy::kForegroundOnly},
149 {"UserVisible/kForegroundOnly/kNone", TaskPriority::USER_VISIBLE,
150 CanRunPolicy::kForegroundOnly, CanRunPolicy::kNone},
151 {"UserVisible/kAll/kNone", TaskPriority::USER_VISIBLE, CanRunPolicy::kAll,
152 CanRunPolicy::kNone}};
153
154 for (auto& test_case : kTestCases) {
155 SCOPED_TRACE(test_case.descriptor);
156
157 task_tracker->SetCanRunPolicy(test_case.allow_policy);
158 target->DidUpdateCanRunPolicy();
159
160 const auto task_runner = create_task_runner(test_case.priority);
161
162 // Post less tasks on iOS to avoid timeouts.
163 const size_t kLargeNumber =
164 #if BUILDFLAG(IS_IOS)
165 16;
166 #else
167 256;
168 #endif
169 for (size_t i = 0; i < kLargeNumber; ++i)
170 task_runner->PostTask(FROM_HERE, DoNothing());
171
172 // Change the CanRunPolicy concurrently with running tasks.
173 // This should not cause crashes.
174 for (size_t i = 0; i < kLargeNumber; ++i) {
175 task_tracker->SetCanRunPolicy(test_case.disallow_policy);
176 target->DidUpdateCanRunPolicy();
177
178 task_tracker->SetCanRunPolicy(test_case.allow_policy);
179 target->DidUpdateCanRunPolicy();
180 }
181
182 task_tracker->FlushForTesting();
183 }
184 }
185
186 } // namespace test
187 } // namespace internal
188 } // namespace base
189
190 #endif // BASE_TASK_THREAD_POOL_CAN_RUN_POLICY_TEST_H_
191