• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/cancelable_task_tracker.h"
6 
7 #include <cstddef>
8 #include <deque>
9 
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/test/test_simple_task_runner.h"
19 #include "base/threading/thread.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 
22 namespace base {
23 
24 namespace {
25 
26 class CancelableTaskTrackerTest : public testing::Test {
27  protected:
~CancelableTaskTrackerTest()28   virtual ~CancelableTaskTrackerTest() { RunCurrentLoopUntilIdle(); }
29 
RunCurrentLoopUntilIdle()30   void RunCurrentLoopUntilIdle() {
31     RunLoop run_loop;
32     run_loop.RunUntilIdle();
33   }
34 
35   CancelableTaskTracker task_tracker_;
36 
37  private:
38   // Needed by CancelableTaskTracker methods.
39   MessageLoop message_loop_;
40 };
41 
AddFailureAt(const tracked_objects::Location & location)42 void AddFailureAt(const tracked_objects::Location& location) {
43   ADD_FAILURE_AT(location.file_name(), location.line_number());
44 }
45 
46 // Returns a closure that fails if run.
MakeExpectedNotRunClosure(const tracked_objects::Location & location)47 Closure MakeExpectedNotRunClosure(const tracked_objects::Location& location) {
48   return Bind(&AddFailureAt, location);
49 }
50 
51 // A helper class for MakeExpectedRunClosure() that fails if it is
52 // destroyed without Run() having been called.  This class may be used
53 // from multiple threads as long as Run() is called at most once
54 // before destruction.
55 class RunChecker {
56  public:
RunChecker(const tracked_objects::Location & location)57   explicit RunChecker(const tracked_objects::Location& location)
58       : location_(location), called_(false) {}
59 
~RunChecker()60   ~RunChecker() {
61     if (!called_) {
62       ADD_FAILURE_AT(location_.file_name(), location_.line_number());
63     }
64   }
65 
Run()66   void Run() { called_ = true; }
67 
68  private:
69   tracked_objects::Location location_;
70   bool called_;
71 };
72 
73 // Returns a closure that fails on destruction if it hasn't been run.
MakeExpectedRunClosure(const tracked_objects::Location & location)74 Closure MakeExpectedRunClosure(const tracked_objects::Location& location) {
75   return Bind(&RunChecker::Run, Owned(new RunChecker(location)));
76 }
77 
78 }  // namespace
79 
80 // With the task tracker, post a task, a task with a reply, and get a
81 // new task id without canceling any of them.  The tasks and the reply
82 // should run and the "is canceled" callback should return false.
TEST_F(CancelableTaskTrackerTest,NoCancel)83 TEST_F(CancelableTaskTrackerTest, NoCancel) {
84   Thread worker_thread("worker thread");
85   ASSERT_TRUE(worker_thread.Start());
86 
87   ignore_result(task_tracker_.PostTask(worker_thread.message_loop_proxy().get(),
88                                        FROM_HERE,
89                                        MakeExpectedRunClosure(FROM_HERE)));
90 
91   ignore_result(
92       task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(),
93                                      FROM_HERE,
94                                      MakeExpectedRunClosure(FROM_HERE),
95                                      MakeExpectedRunClosure(FROM_HERE)));
96 
97   CancelableTaskTracker::IsCanceledCallback is_canceled;
98   ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
99 
100   worker_thread.Stop();
101 
102   RunCurrentLoopUntilIdle();
103 
104   EXPECT_FALSE(is_canceled.Run());
105 }
106 
107 // Post a task with the task tracker but cancel it before running the
108 // task runner.  The task should not run.
TEST_F(CancelableTaskTrackerTest,CancelPostedTask)109 TEST_F(CancelableTaskTrackerTest, CancelPostedTask) {
110   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
111       new TestSimpleTaskRunner());
112 
113   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
114       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
115   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
116 
117   EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size());
118 
119   task_tracker_.TryCancel(task_id);
120 
121   test_task_runner->RunUntilIdle();
122 }
123 
124 // Post a task with reply with the task tracker and cancel it before
125 // running the task runner.  Neither the task nor the reply should
126 // run.
TEST_F(CancelableTaskTrackerTest,CancelPostedTaskAndReply)127 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) {
128   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
129       new TestSimpleTaskRunner());
130 
131   CancelableTaskTracker::TaskId task_id =
132       task_tracker_.PostTaskAndReply(test_task_runner.get(),
133                                      FROM_HERE,
134                                      MakeExpectedNotRunClosure(FROM_HERE),
135                                      MakeExpectedNotRunClosure(FROM_HERE));
136   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
137 
138   task_tracker_.TryCancel(task_id);
139 
140   test_task_runner->RunUntilIdle();
141 }
142 
143 // Post a task with reply with the task tracker and cancel it after
144 // running the task runner but before running the current message
145 // loop.  The task should run but the reply should not.
TEST_F(CancelableTaskTrackerTest,CancelReply)146 TEST_F(CancelableTaskTrackerTest, CancelReply) {
147   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
148       new TestSimpleTaskRunner());
149 
150   CancelableTaskTracker::TaskId task_id =
151       task_tracker_.PostTaskAndReply(test_task_runner.get(),
152                                      FROM_HERE,
153                                      MakeExpectedRunClosure(FROM_HERE),
154                                      MakeExpectedNotRunClosure(FROM_HERE));
155   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
156 
157   test_task_runner->RunUntilIdle();
158 
159   task_tracker_.TryCancel(task_id);
160 }
161 
162 // Post a task with reply with the task tracker on a worker thread and
163 // cancel it before running the current message loop.  The task should
164 // run but the reply should not.
TEST_F(CancelableTaskTrackerTest,CancelReplyDifferentThread)165 TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) {
166   Thread worker_thread("worker thread");
167   ASSERT_TRUE(worker_thread.Start());
168 
169   CancelableTaskTracker::TaskId task_id =
170       task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(),
171                                      FROM_HERE,
172                                      Bind(&DoNothing),
173                                      MakeExpectedNotRunClosure(FROM_HERE));
174   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
175 
176   task_tracker_.TryCancel(task_id);
177 
178   worker_thread.Stop();
179 }
180 
ExpectIsCanceled(const CancelableTaskTracker::IsCanceledCallback & is_canceled,bool expected_is_canceled)181 void ExpectIsCanceled(
182     const CancelableTaskTracker::IsCanceledCallback& is_canceled,
183     bool expected_is_canceled) {
184   EXPECT_EQ(expected_is_canceled, is_canceled.Run());
185 }
186 
187 // Create a new task ID and check its status on a separate thread
188 // before and after canceling.  The is-canceled callback should be
189 // thread-safe (i.e., nothing should blow up).
TEST_F(CancelableTaskTrackerTest,NewTrackedTaskIdDifferentThread)190 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) {
191   CancelableTaskTracker::IsCanceledCallback is_canceled;
192   CancelableTaskTracker::TaskId task_id =
193       task_tracker_.NewTrackedTaskId(&is_canceled);
194 
195   EXPECT_FALSE(is_canceled.Run());
196 
197   Thread other_thread("other thread");
198   ASSERT_TRUE(other_thread.Start());
199   other_thread.message_loop_proxy()->PostTask(
200       FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, false));
201   other_thread.Stop();
202 
203   task_tracker_.TryCancel(task_id);
204 
205   ASSERT_TRUE(other_thread.Start());
206   other_thread.message_loop_proxy()->PostTask(
207       FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true));
208   other_thread.Stop();
209 }
210 
211 // With the task tracker, post a task, a task with a reply, get a new
212 // task id, and then cancel all of them.  None of the tasks nor the
213 // reply should run and the "is canceled" callback should return
214 // true.
TEST_F(CancelableTaskTrackerTest,CancelAll)215 TEST_F(CancelableTaskTrackerTest, CancelAll) {
216   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
217       new TestSimpleTaskRunner());
218 
219   ignore_result(task_tracker_.PostTask(
220       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
221 
222   ignore_result(
223       task_tracker_.PostTaskAndReply(test_task_runner.get(),
224                                      FROM_HERE,
225                                      MakeExpectedNotRunClosure(FROM_HERE),
226                                      MakeExpectedNotRunClosure(FROM_HERE)));
227 
228   CancelableTaskTracker::IsCanceledCallback is_canceled;
229   ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
230 
231   task_tracker_.TryCancelAll();
232 
233   test_task_runner->RunUntilIdle();
234 
235   RunCurrentLoopUntilIdle();
236 
237   EXPECT_TRUE(is_canceled.Run());
238 }
239 
240 // With the task tracker, post a task, a task with a reply, get a new
241 // task id, and then cancel all of them.  None of the tasks nor the
242 // reply should run and the "is canceled" callback should return
243 // true.
TEST_F(CancelableTaskTrackerTest,DestructionCancelsAll)244 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) {
245   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
246       new TestSimpleTaskRunner());
247 
248   CancelableTaskTracker::IsCanceledCallback is_canceled;
249 
250   {
251     // Create another task tracker with a smaller scope.
252     CancelableTaskTracker task_tracker;
253 
254     ignore_result(task_tracker.PostTask(test_task_runner.get(),
255                                         FROM_HERE,
256                                         MakeExpectedNotRunClosure(FROM_HERE)));
257 
258     ignore_result(
259         task_tracker.PostTaskAndReply(test_task_runner.get(),
260                                       FROM_HERE,
261                                       MakeExpectedNotRunClosure(FROM_HERE),
262                                       MakeExpectedNotRunClosure(FROM_HERE)));
263 
264     ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
265   }
266 
267   test_task_runner->RunUntilIdle();
268 
269   RunCurrentLoopUntilIdle();
270 
271   EXPECT_FALSE(is_canceled.Run());
272 }
273 
274 // Post a task and cancel it.  HasTrackedTasks() should return true
275 // from when the task is posted until the (do-nothing) reply task is
276 // flushed.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPost)277 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) {
278   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
279       new TestSimpleTaskRunner());
280 
281   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
282 
283   ignore_result(task_tracker_.PostTask(
284       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
285 
286   task_tracker_.TryCancelAll();
287 
288   test_task_runner->RunUntilIdle();
289 
290   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
291 
292   RunCurrentLoopUntilIdle();
293 
294   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
295 }
296 
297 // Post a task with a reply and cancel it.  HasTrackedTasks() should
298 // return true from when the task is posted until it is canceled.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPostWithReply)299 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReply) {
300   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
301       new TestSimpleTaskRunner());
302 
303   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
304 
305   ignore_result(
306       task_tracker_.PostTaskAndReply(test_task_runner.get(),
307                                      FROM_HERE,
308                                      MakeExpectedNotRunClosure(FROM_HERE),
309                                      MakeExpectedNotRunClosure(FROM_HERE)));
310 
311   task_tracker_.TryCancelAll();
312 
313   test_task_runner->RunUntilIdle();
314 
315   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
316 
317   RunCurrentLoopUntilIdle();
318 
319   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
320 }
321 
322 // Create a new tracked task ID.  HasTrackedTasks() should return true
323 // until the IsCanceledCallback is destroyed.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksIsCancelled)324 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelled) {
325   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
326 
327   CancelableTaskTracker::IsCanceledCallback is_canceled;
328   ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
329 
330   task_tracker_.TryCancelAll();
331 
332   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
333 
334   is_canceled.Reset();
335 
336   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
337 }
338 
339 // The death tests below make sure that calling task tracker member
340 // functions from a thread different from its owner thread DCHECKs in
341 // debug mode.
342 
343 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest {
344  protected:
CancelableTaskTrackerDeathTest()345   CancelableTaskTrackerDeathTest() {
346     // The default style "fast" does not support multi-threaded tests.
347     ::testing::FLAGS_gtest_death_test_style = "threadsafe";
348   }
349 
~CancelableTaskTrackerDeathTest()350   virtual ~CancelableTaskTrackerDeathTest() {}
351 };
352 
353 // Duplicated from base/threading/thread_checker.h so that we can be
354 // good citizens there and undef the macro.
355 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
356 #define ENABLE_THREAD_CHECKER 1
357 #else
358 #define ENABLE_THREAD_CHECKER 0
359 #endif
360 
361 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
MaybeRunDeadlyTaskTrackerMemberFunction(CancelableTaskTracker * task_tracker,const Callback<void (CancelableTaskTracker *)> & fn)362 void MaybeRunDeadlyTaskTrackerMemberFunction(
363     CancelableTaskTracker* task_tracker,
364     const Callback<void(CancelableTaskTracker*)>& fn) {
365 // CancelableTask uses DCHECKs with its ThreadChecker (itself only
366 // enabled in debug mode).
367 #if ENABLE_THREAD_CHECKER
368   EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), "");
369 #endif
370 }
371 
PostDoNothingTask(CancelableTaskTracker * task_tracker)372 void PostDoNothingTask(CancelableTaskTracker* task_tracker) {
373   ignore_result(task_tracker->PostTask(
374       scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(),
375       FROM_HERE,
376       Bind(&DoNothing)));
377 }
378 
TEST_F(CancelableTaskTrackerDeathTest,PostFromDifferentThread)379 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) {
380   Thread bad_thread("bad thread");
381   ASSERT_TRUE(bad_thread.Start());
382 
383   bad_thread.message_loop_proxy()->PostTask(
384       FROM_HERE,
385       Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
386            Unretained(&task_tracker_),
387            Bind(&PostDoNothingTask)));
388 }
389 
TryCancel(CancelableTaskTracker::TaskId task_id,CancelableTaskTracker * task_tracker)390 void TryCancel(CancelableTaskTracker::TaskId task_id,
391                CancelableTaskTracker* task_tracker) {
392   task_tracker->TryCancel(task_id);
393 }
394 
TEST_F(CancelableTaskTrackerDeathTest,CancelOnDifferentThread)395 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) {
396   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
397       new TestSimpleTaskRunner());
398 
399   Thread bad_thread("bad thread");
400   ASSERT_TRUE(bad_thread.Start());
401 
402   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
403       test_task_runner.get(), FROM_HERE, Bind(&DoNothing));
404   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
405 
406   bad_thread.message_loop_proxy()->PostTask(
407       FROM_HERE,
408       Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
409            Unretained(&task_tracker_),
410            Bind(&TryCancel, task_id)));
411 
412   test_task_runner->RunUntilIdle();
413 }
414 
TEST_F(CancelableTaskTrackerDeathTest,CancelAllOnDifferentThread)415 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) {
416   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
417       new TestSimpleTaskRunner());
418 
419   Thread bad_thread("bad thread");
420   ASSERT_TRUE(bad_thread.Start());
421 
422   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
423       test_task_runner.get(), FROM_HERE, Bind(&DoNothing));
424   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
425 
426   bad_thread.message_loop_proxy()->PostTask(
427       FROM_HERE,
428       Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
429            Unretained(&task_tracker_),
430            Bind(&CancelableTaskTracker::TryCancelAll)));
431 
432   test_task_runner->RunUntilIdle();
433 }
434 
435 }  // namespace base
436