OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
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/task_scheduler/task_tracker.h" | |
6 | |
7 #include <queue> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/macros.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/synchronization/waitable_event.h" | |
14 #include "base/task_scheduler/task.h" | |
15 #include "base/task_scheduler/task_traits.h" | |
16 #include "base/threading/platform_thread.h" | |
17 #include "base/threading/simple_thread.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 namespace base { | |
21 namespace internal { | |
22 | |
23 namespace { | |
24 | |
25 class ThreadCallingShutdown : public SimpleThread { | |
26 public: | |
27 explicit ThreadCallingShutdown(TaskTracker* tracker) | |
28 : SimpleThread("ThreadCallingShutdown"), tracker_(tracker) {} | |
29 | |
30 // Returns true once the call to Shutdown() has returned. | |
31 bool has_returned() const { return has_returned_; } | |
32 | |
33 private: | |
34 void Run() override { | |
35 tracker_->Shutdown(); | |
36 has_returned_ = true; | |
37 } | |
38 | |
39 TaskTracker* const tracker_; | |
40 bool has_returned_ = false; | |
41 | |
42 DISALLOW_COPY_AND_ASSIGN(ThreadCallingShutdown); | |
43 }; | |
44 | |
45 class TaskSchedulerTaskTrackerTest | |
46 : public testing::TestWithParam<TaskShutdownBehavior> { | |
47 public: | |
48 TaskSchedulerTaskTrackerTest() = default; | |
49 | |
50 void RunNextPostedTaskViaTracker() { | |
51 ASSERT_FALSE(posted_tasks_.empty()); | |
52 tracker_.RunTask(posted_tasks_.front().get()); | |
53 posted_tasks_.pop(); | |
54 } | |
55 | |
56 protected: | |
57 // Creates a task with |shutdown_behavior|. | |
58 scoped_ptr<Task> CreateTask(TaskShutdownBehavior shutdown_behavior) { | |
59 return make_scoped_ptr(new Task( | |
60 FROM_HERE, | |
61 Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)), | |
62 TaskTraits().WithShutdownBehavior(shutdown_behavior))); | |
63 } | |
64 | |
65 // Tries to post |task| via |tracker_|. If |tracker_| approves the operation, | |
66 // |task| is added to |posted_tasks_|. | |
67 void PostTaskViaTracker(scoped_ptr<Task> task) { | |
68 tracker_.PostTask( | |
69 Bind(&TaskSchedulerTaskTrackerTest::PostTaskCallback, Unretained(this)), | |
70 std::move(task)); | |
71 } | |
72 | |
73 // Calls tracker_->Shutdown() on a new thread. When this returns, the | |
74 // Shutdown() method has been entered on the new thread, but it hasn't | |
75 // necessarily returned. | |
76 void CallShutdownAsync() { | |
77 DCHECK(!thread_calling_shutdown_.get()); | |
78 thread_calling_shutdown_.reset(new ThreadCallingShutdown(&tracker_)); | |
79 thread_calling_shutdown_->Start(); | |
80 while (!tracker_.is_shutting_down_for_testing() && | |
81 !tracker_.shutdown_completed()) { | |
82 PlatformThread::YieldCurrentThread(); | |
83 } | |
84 } | |
85 | |
86 void WaitForAsyncShutdownCompleted() { | |
87 DCHECK(thread_calling_shutdown_.get()); | |
88 thread_calling_shutdown_->Join(); | |
89 EXPECT_TRUE(thread_calling_shutdown_->has_returned()); | |
90 EXPECT_TRUE(tracker_.shutdown_completed()); | |
91 } | |
92 | |
93 void VerifyAsyncShutdownInProgress() { | |
94 DCHECK(thread_calling_shutdown_.get()); | |
95 EXPECT_FALSE(thread_calling_shutdown_->has_returned()); | |
96 EXPECT_FALSE(tracker_.shutdown_completed()); | |
97 EXPECT_TRUE(tracker_.is_shutting_down_for_testing()); | |
98 } | |
99 | |
100 TaskTracker tracker_; | |
101 size_t num_tasks_executed_ = 0; | |
102 std::queue<scoped_ptr<Task>> posted_tasks_; | |
103 | |
104 private: | |
105 void PostTaskCallback(scoped_ptr<Task> task) { | |
106 posted_tasks_.push(std::move(task)); | |
107 } | |
108 | |
109 void RunTaskCallback() { ++num_tasks_executed_; } | |
110 | |
111 scoped_ptr<ThreadCallingShutdown> thread_calling_shutdown_; | |
112 | |
113 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest); | |
114 }; | |
115 | |
116 // A thread which calls | |
117 // TaskSchedulerTaskTrackerTest::RunNextPostedTaskViaTracker() asynchronously. | |
118 class ThreadRunningNextPostedTask : public SimpleThread { | |
119 public: | |
120 explicit ThreadRunningNextPostedTask(TaskSchedulerTaskTrackerTest* test) | |
121 : SimpleThread("ThreadRunningNextPostedTask"), test_(test) {} | |
122 | |
123 private: | |
124 void Run() override { test_->RunNextPostedTaskViaTracker(); } | |
125 | |
126 TaskSchedulerTaskTrackerTest* const test_; | |
127 | |
128 DISALLOW_COPY_AND_ASSIGN(ThreadRunningNextPostedTask); | |
129 }; | |
130 | |
131 } // namespace | |
132 | |
133 TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunBeforeShutdown) { | |
134 scoped_ptr<Task> task_to_post(CreateTask(GetParam())); | |
135 const Task* task_to_post_raw = task_to_post.get(); | |
136 | |
137 // Post the task. | |
138 EXPECT_TRUE(posted_tasks_.empty()); | |
139 PostTaskViaTracker(std::move(task_to_post)); | |
140 ASSERT_EQ(1, posted_tasks_.size()); | |
gab
2016/03/17 01:42:12
1U (prefer explicitly unsigned constants to compar
fdoray
2016/03/17 20:12:16
Done.
| |
141 EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); | |
142 | |
143 // Run the posted task. | |
144 EXPECT_EQ(0U, num_tasks_executed_); | |
145 RunNextPostedTaskViaTracker(); | |
146 EXPECT_EQ(1U, num_tasks_executed_); | |
147 | |
148 // Shutdown() shouldn't block. | |
149 tracker_.Shutdown(); | |
150 } | |
151 | |
152 TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunLongTaskBeforeShutdown) { | |
153 // Post a task that will block until |event| is signaled. | |
154 EXPECT_TRUE(posted_tasks_.empty()); | |
155 WaitableEvent event(false, false); | |
156 PostTaskViaTracker(make_scoped_ptr( | |
157 new Task(FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event)), | |
158 TaskTraits().WithShutdownBehavior(GetParam())))); | |
159 ASSERT_EQ(1, posted_tasks_.size()); | |
160 | |
161 // Run the task asynchronouly. | |
162 ThreadRunningNextPostedTask thread(this); | |
163 thread.Start(); | |
164 | |
165 // Initiate shutdown while the task is running. | |
166 CallShutdownAsync(); | |
167 | |
168 if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) { | |
169 // Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress. | |
170 WaitForAsyncShutdownCompleted(); | |
171 } else { | |
172 // Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress. | |
173 VerifyAsyncShutdownInProgress(); | |
174 } | |
175 | |
176 // Unblock the task. | |
177 event.Signal(); | |
178 thread.Join(); | |
179 | |
180 if (GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) | |
181 WaitForAsyncShutdownCompleted(); | |
182 } | |
183 | |
184 TEST_P(TaskSchedulerTaskTrackerTest, PostBeforeShutdownRunDuringShutdown) { | |
185 scoped_ptr<Task> task_to_post(CreateTask(GetParam())); | |
186 const Task* task_to_post_raw = task_to_post.get(); | |
187 | |
188 // Post the task. | |
189 EXPECT_TRUE(posted_tasks_.empty()); | |
190 PostTaskViaTracker(std::move(task_to_post)); | |
191 ASSERT_EQ(1, posted_tasks_.size()); | |
192 EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); | |
193 | |
194 // Post a BLOCK_SHUTDOWN task just to block shutdown. | |
195 PostTaskViaTracker(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)); | |
196 | |
197 // Call Shutdown() asynchronously. | |
198 CallShutdownAsync(); | |
199 VerifyAsyncShutdownInProgress(); | |
200 | |
201 // Try to run task posted at the beginning of this test. It should only | |
202 // succeed for a BLOCK_SHUTDOWN task. | |
gab
2016/03/17 01:42:13
s/It should only succeed for a BLOCK_SHUTDOWN task
fdoray
2016/03/17 20:12:16
Done.
| |
203 EXPECT_EQ(0U, num_tasks_executed_); | |
204 RunNextPostedTaskViaTracker(); | |
205 EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 1U : 0U, | |
206 num_tasks_executed_); | |
207 VerifyAsyncShutdownInProgress(); | |
208 | |
209 // Unblock shutdown by running the remaining BLOCK_SHUTDOWN task. | |
210 RunNextPostedTaskViaTracker(); | |
211 EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U, | |
212 num_tasks_executed_); | |
213 WaitForAsyncShutdownCompleted(); | |
214 } | |
215 | |
216 TEST_P(TaskSchedulerTaskTrackerTest, PostBeforeShutdownRunAfterShutdown) { | |
217 scoped_ptr<Task> task_to_post(CreateTask(GetParam())); | |
218 const Task* task_to_post_raw = task_to_post.get(); | |
219 | |
220 // Post the task. | |
221 EXPECT_TRUE(posted_tasks_.empty()); | |
222 PostTaskViaTracker(std::move(task_to_post)); | |
223 ASSERT_EQ(1, posted_tasks_.size()); | |
224 ASSERT_EQ(task_to_post_raw, posted_tasks_.front().get()); | |
225 | |
226 // Call Shutdown() asynchronously. | |
227 CallShutdownAsync(); | |
228 EXPECT_EQ(0U, num_tasks_executed_); | |
229 | |
230 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) { | |
231 VerifyAsyncShutdownInProgress(); | |
232 | |
233 // Run the task to unblock shutdown. | |
234 RunNextPostedTaskViaTracker(); | |
235 EXPECT_EQ(1U, num_tasks_executed_); | |
236 WaitForAsyncShutdownCompleted(); | |
237 | |
238 // Running a BLOCK_SHUTDOWN task after shutdown should fail. | |
239 if (DCHECK_IS_ON()) { | |
240 EXPECT_DEATH( | |
gab
2016/03/17 01:42:13
Use EXPECT_DCHECK_DEATH macro from Rob's Scheduler
fdoray
2016/03/17 20:12:16
Done.
| |
241 { | |
242 tracker_.RunTask( | |
243 CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN).get()); | |
gab
2016/03/17 01:42:12
Since that task was never posted to the tracker, i
fdoray
2016/03/17 20:12:16
There is no way to test running after shutdown a B
| |
244 }, | |
245 ""); | |
246 } | |
247 } else { | |
248 WaitForAsyncShutdownCompleted(); | |
249 | |
250 // The task shouldn't be allowed to run after shutdown. | |
251 RunNextPostedTaskViaTracker(); | |
252 EXPECT_EQ(0U, num_tasks_executed_); | |
253 } | |
254 } | |
255 | |
256 TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunDuringShutdown) { | |
257 // Post a BLOCK_SHUTDOWN task just to block shutdown. | |
258 PostTaskViaTracker(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)); | |
259 scoped_ptr<Task> block_shutdown_task = std::move(posted_tasks_.front()); | |
260 posted_tasks_.pop(); | |
261 | |
262 // Call Shutdown() asynchronously. | |
263 CallShutdownAsync(); | |
264 VerifyAsyncShutdownInProgress(); | |
265 | |
266 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) { | |
267 // Post a task. This should succeed. | |
268 EXPECT_TRUE(posted_tasks_.empty()); | |
269 PostTaskViaTracker(CreateTask(GetParam())); | |
270 EXPECT_EQ(1U, posted_tasks_.size()); | |
271 | |
272 // Run the task. This should succeed. | |
273 EXPECT_EQ(0U, num_tasks_executed_); | |
274 RunNextPostedTaskViaTracker(); | |
275 EXPECT_EQ(1U, num_tasks_executed_); | |
276 } else { | |
277 // It shouldn't be possible to post a task which isn't BLOCK_SHUTDOWN. | |
278 PostTaskViaTracker(CreateTask(GetParam())); | |
279 EXPECT_TRUE(posted_tasks_.empty()); | |
280 | |
281 // Don't try to run the task, because it hasn't been posted successfully. | |
282 } | |
283 | |
284 // Unblock shutdown by running the BLOCK_SHUTDOWN task posted at the beginning | |
285 // of the test. | |
286 VerifyAsyncShutdownInProgress(); | |
287 tracker_.RunTask(block_shutdown_task.get()); | |
288 EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U, | |
289 num_tasks_executed_); | |
290 WaitForAsyncShutdownCompleted(); | |
291 } | |
292 | |
293 TEST_P(TaskSchedulerTaskTrackerTest, PostAfterShutdown) { | |
294 // It is not possible to post a task after shutdown. | |
295 tracker_.Shutdown(); | |
296 EXPECT_TRUE(posted_tasks_.empty()); | |
297 PostTaskViaTracker(CreateTask(GetParam())); | |
298 EXPECT_TRUE(posted_tasks_.empty()); | |
299 } | |
300 | |
301 INSTANTIATE_TEST_CASE_P( | |
302 ContinueOnShutdown, | |
303 TaskSchedulerTaskTrackerTest, | |
304 ::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)); | |
305 INSTANTIATE_TEST_CASE_P( | |
306 SkipOnShutdown, | |
307 TaskSchedulerTaskTrackerTest, | |
308 ::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)); | |
309 INSTANTIATE_TEST_CASE_P( | |
310 BlockShutdown, | |
311 TaskSchedulerTaskTrackerTest, | |
312 ::testing::Values(TaskShutdownBehavior::BLOCK_SHUTDOWN)); | |
313 | |
314 } // namespace internal | |
315 } // namespace base | |
OLD | NEW |