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/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:
     28   ~CancelableTaskTrackerTest() override { 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.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.
    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.
    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.
    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.
    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 
    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).
    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.
    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.
    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.
    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.
    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.
    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:
    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.
    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 
    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 
    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 
    382 void TryCancel(CancelableTaskTracker::TaskId task_id,
    383                CancelableTaskTracker* task_tracker) {
    384   task_tracker->TryCancel(task_id);
    385 }
    386 
    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 
    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