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