• 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/run_loop.h"
17 #include "base/single_thread_task_runner.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   ~CancelableTaskTrackerTest() override { 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.task_runner().get(),
88                                        FROM_HERE,
89                                        MakeExpectedRunClosure(FROM_HERE)));
90 
91   ignore_result(task_tracker_.PostTaskAndReply(
92       worker_thread.task_runner().get(), FROM_HERE,
93       MakeExpectedRunClosure(FROM_HERE), MakeExpectedRunClosure(FROM_HERE)));
94 
95   CancelableTaskTracker::IsCanceledCallback is_canceled;
96   ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
97 
98   worker_thread.Stop();
99 
100   RunCurrentLoopUntilIdle();
101 
102   EXPECT_FALSE(is_canceled.Run());
103 }
104 
105 // Post a task with the task tracker but cancel it before running the
106 // task runner.  The task should not run.
TEST_F(CancelableTaskTrackerTest,CancelPostedTask)107 TEST_F(CancelableTaskTrackerTest, CancelPostedTask) {
108   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
109       new TestSimpleTaskRunner());
110 
111   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
112       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
113   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
114 
115   EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size());
116 
117   task_tracker_.TryCancel(task_id);
118 
119   test_task_runner->RunUntilIdle();
120 }
121 
122 // Post a task with reply with the task tracker and cancel it before
123 // running the task runner.  Neither the task nor the reply should
124 // run.
TEST_F(CancelableTaskTrackerTest,CancelPostedTaskAndReply)125 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) {
126   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
127       new TestSimpleTaskRunner());
128 
129   CancelableTaskTracker::TaskId task_id =
130       task_tracker_.PostTaskAndReply(test_task_runner.get(),
131                                      FROM_HERE,
132                                      MakeExpectedNotRunClosure(FROM_HERE),
133                                      MakeExpectedNotRunClosure(FROM_HERE));
134   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
135 
136   task_tracker_.TryCancel(task_id);
137 
138   test_task_runner->RunUntilIdle();
139 }
140 
141 // Post a task with reply with the task tracker and cancel it after
142 // running the task runner but before running the current message
143 // loop.  The task should run but the reply should not.
TEST_F(CancelableTaskTrackerTest,CancelReply)144 TEST_F(CancelableTaskTrackerTest, CancelReply) {
145   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
146       new TestSimpleTaskRunner());
147 
148   CancelableTaskTracker::TaskId task_id =
149       task_tracker_.PostTaskAndReply(test_task_runner.get(),
150                                      FROM_HERE,
151                                      MakeExpectedRunClosure(FROM_HERE),
152                                      MakeExpectedNotRunClosure(FROM_HERE));
153   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
154 
155   test_task_runner->RunUntilIdle();
156 
157   task_tracker_.TryCancel(task_id);
158 }
159 
160 // Post a task with reply with the task tracker on a worker thread and
161 // cancel it before running the current message loop.  The task should
162 // run but the reply should not.
TEST_F(CancelableTaskTrackerTest,CancelReplyDifferentThread)163 TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) {
164   Thread worker_thread("worker thread");
165   ASSERT_TRUE(worker_thread.Start());
166 
167   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTaskAndReply(
168       worker_thread.task_runner().get(), FROM_HERE, Bind(&DoNothing),
169       MakeExpectedNotRunClosure(FROM_HERE));
170   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
171 
172   task_tracker_.TryCancel(task_id);
173 
174   worker_thread.Stop();
175 }
176 
ExpectIsCanceled(const CancelableTaskTracker::IsCanceledCallback & is_canceled,bool expected_is_canceled)177 void ExpectIsCanceled(
178     const CancelableTaskTracker::IsCanceledCallback& is_canceled,
179     bool expected_is_canceled) {
180   EXPECT_EQ(expected_is_canceled, is_canceled.Run());
181 }
182 
183 // Create a new task ID and check its status on a separate thread
184 // before and after canceling.  The is-canceled callback should be
185 // thread-safe (i.e., nothing should blow up).
TEST_F(CancelableTaskTrackerTest,NewTrackedTaskIdDifferentThread)186 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) {
187   CancelableTaskTracker::IsCanceledCallback is_canceled;
188   CancelableTaskTracker::TaskId task_id =
189       task_tracker_.NewTrackedTaskId(&is_canceled);
190 
191   EXPECT_FALSE(is_canceled.Run());
192 
193   Thread other_thread("other thread");
194   ASSERT_TRUE(other_thread.Start());
195   other_thread.task_runner()->PostTask(
196       FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, false));
197   other_thread.Stop();
198 
199   task_tracker_.TryCancel(task_id);
200 
201   ASSERT_TRUE(other_thread.Start());
202   other_thread.task_runner()->PostTask(
203       FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true));
204   other_thread.Stop();
205 }
206 
207 // With the task tracker, post a task, a task with a reply, get a new
208 // task id, and then cancel all of them.  None of the tasks nor the
209 // reply should run and the "is canceled" callback should return
210 // true.
TEST_F(CancelableTaskTrackerTest,CancelAll)211 TEST_F(CancelableTaskTrackerTest, CancelAll) {
212   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
213       new TestSimpleTaskRunner());
214 
215   ignore_result(task_tracker_.PostTask(
216       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
217 
218   ignore_result(
219       task_tracker_.PostTaskAndReply(test_task_runner.get(),
220                                      FROM_HERE,
221                                      MakeExpectedNotRunClosure(FROM_HERE),
222                                      MakeExpectedNotRunClosure(FROM_HERE)));
223 
224   CancelableTaskTracker::IsCanceledCallback is_canceled;
225   ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
226 
227   task_tracker_.TryCancelAll();
228 
229   test_task_runner->RunUntilIdle();
230 
231   RunCurrentLoopUntilIdle();
232 
233   EXPECT_TRUE(is_canceled.Run());
234 }
235 
236 // With the task tracker, post a task, a task with a reply, get a new
237 // task id, and then cancel all of them.  None of the tasks nor the
238 // reply should run and the "is canceled" callback should return
239 // true.
TEST_F(CancelableTaskTrackerTest,DestructionCancelsAll)240 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) {
241   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
242       new TestSimpleTaskRunner());
243 
244   CancelableTaskTracker::IsCanceledCallback is_canceled;
245 
246   {
247     // Create another task tracker with a smaller scope.
248     CancelableTaskTracker task_tracker;
249 
250     ignore_result(task_tracker.PostTask(test_task_runner.get(),
251                                         FROM_HERE,
252                                         MakeExpectedNotRunClosure(FROM_HERE)));
253 
254     ignore_result(
255         task_tracker.PostTaskAndReply(test_task_runner.get(),
256                                       FROM_HERE,
257                                       MakeExpectedNotRunClosure(FROM_HERE),
258                                       MakeExpectedNotRunClosure(FROM_HERE)));
259 
260     ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
261   }
262 
263   test_task_runner->RunUntilIdle();
264 
265   RunCurrentLoopUntilIdle();
266 
267   EXPECT_FALSE(is_canceled.Run());
268 }
269 
270 // Post a task and cancel it.  HasTrackedTasks() should return true
271 // from when the task is posted until the (do-nothing) reply task is
272 // flushed.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPost)273 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) {
274   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
275       new TestSimpleTaskRunner());
276 
277   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
278 
279   ignore_result(task_tracker_.PostTask(
280       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
281 
282   task_tracker_.TryCancelAll();
283 
284   test_task_runner->RunUntilIdle();
285 
286   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
287 
288   RunCurrentLoopUntilIdle();
289 
290   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
291 }
292 
293 // Post a task with a reply and cancel it.  HasTrackedTasks() should
294 // return true from when the task is posted until it is canceled.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPostWithReply)295 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReply) {
296   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
297       new TestSimpleTaskRunner());
298 
299   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
300 
301   ignore_result(
302       task_tracker_.PostTaskAndReply(test_task_runner.get(),
303                                      FROM_HERE,
304                                      MakeExpectedNotRunClosure(FROM_HERE),
305                                      MakeExpectedNotRunClosure(FROM_HERE)));
306 
307   task_tracker_.TryCancelAll();
308 
309   test_task_runner->RunUntilIdle();
310 
311   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
312 
313   RunCurrentLoopUntilIdle();
314 
315   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
316 }
317 
318 // Create a new tracked task ID.  HasTrackedTasks() should return true
319 // until the IsCanceledCallback is destroyed.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksIsCancelled)320 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelled) {
321   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
322 
323   CancelableTaskTracker::IsCanceledCallback is_canceled;
324   ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
325 
326   task_tracker_.TryCancelAll();
327 
328   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
329 
330   is_canceled.Reset();
331 
332   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
333 }
334 
335 // The death tests below make sure that calling task tracker member
336 // functions from a thread different from its owner thread DCHECKs in
337 // debug mode.
338 
339 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest {
340  protected:
CancelableTaskTrackerDeathTest()341   CancelableTaskTrackerDeathTest() {
342     // The default style "fast" does not support multi-threaded tests.
343     ::testing::FLAGS_gtest_death_test_style = "threadsafe";
344   }
345 };
346 
347 // Duplicated from base/threading/thread_checker.h so that we can be
348 // good citizens there and undef the macro.
349 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
350 #define ENABLE_THREAD_CHECKER 1
351 #else
352 #define ENABLE_THREAD_CHECKER 0
353 #endif
354 
355 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
MaybeRunDeadlyTaskTrackerMemberFunction(CancelableTaskTracker * task_tracker,const Callback<void (CancelableTaskTracker *)> & fn)356 void MaybeRunDeadlyTaskTrackerMemberFunction(
357     CancelableTaskTracker* task_tracker,
358     const Callback<void(CancelableTaskTracker*)>& fn) {
359 // CancelableTask uses DCHECKs with its ThreadChecker (itself only
360 // enabled in debug mode).
361 #if ENABLE_THREAD_CHECKER
362   EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), "");
363 #endif
364 }
365 
PostDoNothingTask(CancelableTaskTracker * task_tracker)366 void PostDoNothingTask(CancelableTaskTracker* task_tracker) {
367   ignore_result(task_tracker->PostTask(
368       scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(),
369       FROM_HERE,
370       Bind(&DoNothing)));
371 }
372 
TEST_F(CancelableTaskTrackerDeathTest,PostFromDifferentThread)373 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) {
374   Thread bad_thread("bad thread");
375   ASSERT_TRUE(bad_thread.Start());
376 
377   bad_thread.task_runner()->PostTask(
378       FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
379                       Unretained(&task_tracker_), Bind(&PostDoNothingTask)));
380 }
381 
TryCancel(CancelableTaskTracker::TaskId task_id,CancelableTaskTracker * task_tracker)382 void TryCancel(CancelableTaskTracker::TaskId task_id,
383                CancelableTaskTracker* task_tracker) {
384   task_tracker->TryCancel(task_id);
385 }
386 
TEST_F(CancelableTaskTrackerDeathTest,CancelOnDifferentThread)387 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) {
388   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
389       new TestSimpleTaskRunner());
390 
391   Thread bad_thread("bad thread");
392   ASSERT_TRUE(bad_thread.Start());
393 
394   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
395       test_task_runner.get(), FROM_HERE, Bind(&DoNothing));
396   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
397 
398   bad_thread.task_runner()->PostTask(
399       FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
400                       Unretained(&task_tracker_), Bind(&TryCancel, task_id)));
401 
402   test_task_runner->RunUntilIdle();
403 }
404 
TEST_F(CancelableTaskTrackerDeathTest,CancelAllOnDifferentThread)405 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) {
406   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
407       new TestSimpleTaskRunner());
408 
409   Thread bad_thread("bad thread");
410   ASSERT_TRUE(bad_thread.Start());
411 
412   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
413       test_task_runner.get(), FROM_HERE, Bind(&DoNothing));
414   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
415 
416   bad_thread.task_runner()->PostTask(
417       FROM_HERE,
418       Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, Unretained(&task_tracker_),
419            Bind(&CancelableTaskTracker::TryCancelAll)));
420 
421   test_task_runner->RunUntilIdle();
422 }
423 
424 }  // namespace base
425