| Index: test/unittests/cancelable-tasks-unittest.cc | 
| diff --git a/test/unittests/cancelable-tasks-unittest.cc b/test/unittests/cancelable-tasks-unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..70d9b1f28237ea01863f7201e7fa2fb782164b5d | 
| --- /dev/null | 
| +++ b/test/unittests/cancelable-tasks-unittest.cc | 
| @@ -0,0 +1,218 @@ | 
| +// Copyright 2015 the V8 project authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "src/base/atomicops.h" | 
| +#include "src/base/platform/platform.h" | 
| +#include "src/cancelable-task.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| + | 
| +namespace v8 { | 
| +namespace internal { | 
| + | 
| +namespace { | 
| + | 
| +class TestTask : public Task, public Cancelable { | 
| + public: | 
| +  enum Mode { kDoNothing, kWaitTillCanceledAgain, kCheckNotRun }; | 
| + | 
| +  TestTask(CancelableTaskManager* parent, base::AtomicWord* result, | 
| +           Mode mode = kDoNothing) | 
| +      : Cancelable(parent), result_(result), mode_(mode) {} | 
| + | 
| +  // Task overrides. | 
| +  void Run() final { | 
| +    if (TryRun()) { | 
| +      RunInternal(); | 
| +    } | 
| +  } | 
| + | 
| + private: | 
| +  void RunInternal() { | 
| +    base::Release_Store(result_, id()); | 
| + | 
| +    switch (mode_) { | 
| +      case kWaitTillCanceledAgain: | 
| +        // Simple busy wait until the main thread tried to cancel. | 
| +        while (CancelAttempts() == 0) { | 
| +        } | 
| +        break; | 
| +      case kCheckNotRun: | 
| +        // Check that we never execute {RunInternal}. | 
| +        EXPECT_TRUE(false); | 
| +        break; | 
| +      default: | 
| +        break; | 
| +    } | 
| +  } | 
| + | 
| +  base::AtomicWord* result_; | 
| +  Mode mode_; | 
| +}; | 
| + | 
| + | 
| +class SequentialRunner { | 
| + public: | 
| +  explicit SequentialRunner(TestTask* task) : task_(task) {} | 
| + | 
| +  void Run() { | 
| +    task_->Run(); | 
| +    delete task_; | 
| +  } | 
| + | 
| + private: | 
| +  TestTask* task_; | 
| +}; | 
| + | 
| + | 
| +class ThreadedRunner final : public base::Thread { | 
| + public: | 
| +  explicit ThreadedRunner(TestTask* task) | 
| +      : Thread(Options("runner thread")), task_(task) {} | 
| + | 
| +  virtual void Run() { | 
| +    task_->Run(); | 
| +    delete task_; | 
| +  } | 
| + | 
| + private: | 
| +  TestTask* task_; | 
| +}; | 
| + | 
| + | 
| +typedef base::AtomicWord ResultType; | 
| + | 
| + | 
| +intptr_t GetValue(ResultType* result) { return base::Acquire_Load(result); } | 
| + | 
| +}  // namespace | 
| + | 
| + | 
| +TEST(CancelableTask, EmptyCancelableTaskManager) { | 
| +  CancelableTaskManager manager; | 
| +  manager.CancelAndWait(); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, SequentialCancelAndWait) { | 
| +  CancelableTaskManager manager; | 
| +  ResultType result1 = 0; | 
| +  SequentialRunner runner1( | 
| +      new TestTask(&manager, &result1, TestTask::kCheckNotRun)); | 
| +  EXPECT_EQ(GetValue(&result1), 0); | 
| +  manager.CancelAndWait(); | 
| +  EXPECT_EQ(GetValue(&result1), 0); | 
| +  runner1.Run();  // Run to avoid leaking the Task. | 
| +  EXPECT_EQ(GetValue(&result1), 0); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, SequentialMultipleTasks) { | 
| +  CancelableTaskManager manager; | 
| +  ResultType result1 = 0; | 
| +  ResultType result2 = 0; | 
| +  TestTask* task1 = new TestTask(&manager, &result1); | 
| +  TestTask* task2 = new TestTask(&manager, &result2); | 
| +  SequentialRunner runner1(task1); | 
| +  SequentialRunner runner2(task2); | 
| +  EXPECT_EQ(task1->id(), 1); | 
| +  EXPECT_EQ(task2->id(), 2); | 
| + | 
| +  EXPECT_EQ(GetValue(&result1), 0); | 
| +  runner1.Run();  // Don't touch task1 after running it. | 
| +  EXPECT_EQ(GetValue(&result1), 1); | 
| + | 
| +  EXPECT_EQ(GetValue(&result2), 0); | 
| +  runner2.Run();  // Don't touch task2 after running it. | 
| +  EXPECT_EQ(GetValue(&result2), 2); | 
| + | 
| +  manager.CancelAndWait(); | 
| +  EXPECT_FALSE(manager.TryAbort(1)); | 
| +  EXPECT_FALSE(manager.TryAbort(2)); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, ThreadedMultipleTasksStarted) { | 
| +  CancelableTaskManager manager; | 
| +  ResultType result1 = 0; | 
| +  ResultType result2 = 0; | 
| +  TestTask* task1 = | 
| +      new TestTask(&manager, &result1, TestTask::kWaitTillCanceledAgain); | 
| +  TestTask* task2 = | 
| +      new TestTask(&manager, &result2, TestTask::kWaitTillCanceledAgain); | 
| +  ThreadedRunner runner1(task1); | 
| +  ThreadedRunner runner2(task2); | 
| +  runner1.Start(); | 
| +  runner2.Start(); | 
| +  // Busy wait on result to make sure both tasks are done. | 
| +  while ((GetValue(&result1) == 0) || (GetValue(&result2) == 0)) { | 
| +  } | 
| +  manager.CancelAndWait(); | 
| +  runner1.Join(); | 
| +  runner2.Join(); | 
| +  EXPECT_EQ(GetValue(&result1), 1); | 
| +  EXPECT_EQ(GetValue(&result2), 2); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, ThreadedMultipleTasksNotRun) { | 
| +  CancelableTaskManager manager; | 
| +  ResultType result1 = 0; | 
| +  ResultType result2 = 0; | 
| +  TestTask* task1 = new TestTask(&manager, &result1, TestTask::kCheckNotRun); | 
| +  TestTask* task2 = new TestTask(&manager, &result2, TestTask::kCheckNotRun); | 
| +  ThreadedRunner runner1(task1); | 
| +  ThreadedRunner runner2(task2); | 
| +  manager.CancelAndWait(); | 
| +  // Tasks are canceled, hence the runner will bail out and not update result. | 
| +  runner1.Start(); | 
| +  runner2.Start(); | 
| +  runner1.Join(); | 
| +  runner2.Join(); | 
| +  EXPECT_EQ(GetValue(&result1), 0); | 
| +  EXPECT_EQ(GetValue(&result2), 0); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, RemoveBeforeCancelAndWait) { | 
| +  CancelableTaskManager manager; | 
| +  ResultType result1 = 0; | 
| +  TestTask* task1 = new TestTask(&manager, &result1, TestTask::kCheckNotRun); | 
| +  ThreadedRunner runner1(task1); | 
| +  uint32_t id = task1->id(); | 
| +  EXPECT_EQ(id, 1); | 
| +  EXPECT_TRUE(manager.TryAbort(id)); | 
| +  runner1.Start(); | 
| +  runner1.Join(); | 
| +  manager.CancelAndWait(); | 
| +  EXPECT_EQ(GetValue(&result1), 0); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, RemoveAfterCancelAndWait) { | 
| +  CancelableTaskManager manager; | 
| +  ResultType result1 = 0; | 
| +  TestTask* task1 = new TestTask(&manager, &result1); | 
| +  ThreadedRunner runner1(task1); | 
| +  uint32_t id = task1->id(); | 
| +  EXPECT_EQ(id, 1); | 
| +  runner1.Start(); | 
| +  runner1.Join(); | 
| +  manager.CancelAndWait(); | 
| +  EXPECT_FALSE(manager.TryAbort(id)); | 
| +  EXPECT_EQ(GetValue(&result1), 1); | 
| +} | 
| + | 
| + | 
| +TEST(CancelableTask, RemoveUnmanagedId) { | 
| +  CancelableTaskManager manager; | 
| +  EXPECT_FALSE(manager.TryAbort(1)); | 
| +  EXPECT_FALSE(manager.TryAbort(2)); | 
| +  manager.CancelAndWait(); | 
| +  EXPECT_FALSE(manager.TryAbort(1)); | 
| +  EXPECT_FALSE(manager.TryAbort(3)); | 
| +} | 
| + | 
| +}  // namespace internal | 
| +}  // namespace v8 | 
|  |