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