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