Home | History | Annotate | Download | only in task
      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:
     28   virtual ~CancelableTaskTrackerTest() { RunCurrentLoopUntilIdle(); }
     29 
     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 
     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.
     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:
     57   explicit RunChecker(const tracked_objects::Location& location)
     58       : location_(location), called_(false) {}
     59 
     60   ~RunChecker() {
     61     if (!called_) {
     62       ADD_FAILURE_AT(location_.file_name(), location_.line_number());
     63     }
     64   }
     65 
     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.
     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.
     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.
    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.
    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.
    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.
    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 
    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).
    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.
    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.
    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.
    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.
    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.
    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:
    345   CancelableTaskTrackerDeathTest() {
    346     // The default style "fast" does not support multi-threaded tests.
    347     ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    348   }
    349 
    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.
    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 
    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 
    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 
    390 void TryCancel(CancelableTaskTracker::TaskId task_id,
    391                CancelableTaskTracker* task_tracker) {
    392   task_tracker->TryCancel(task_id);
    393 }
    394 
    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 
    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