| 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..9bebc9bb64692e2f7327fc8507dec8e7ec6f9a51
|
| --- /dev/null
|
| +++ b/test/unittests/cancelable-tasks-unittest.cc
|
| @@ -0,0 +1,191 @@
|
| +// 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 MockTask : public Task, public Cancelable {
|
| + public:
|
| + enum Mode { kDoNothing, kWaitTillCanceledAgain, kCheckNotRun };
|
| +
|
| + MockTask(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() {
|
| + *result_ = id();
|
| + 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(MockTask* task) : task_(task) {}
|
| +
|
| + void Run() {
|
| + task_->Run();
|
| + delete task_;
|
| + }
|
| +
|
| + private:
|
| + MockTask* task_;
|
| +};
|
| +
|
| +
|
| +class ThreadedRunner final : public base::Thread {
|
| + public:
|
| + explicit ThreadedRunner(MockTask* task)
|
| + : Thread(Options("runner thread")), task_(task) {}
|
| +
|
| + virtual void Run() {
|
| + task_->Run();
|
| + delete task_;
|
| + }
|
| +
|
| + private:
|
| + MockTask* task_;
|
| +};
|
| +
|
| +
|
| +typedef base::AtomicWord ResultType;
|
| +
|
| +
|
| +intptr_t GetValue(ResultType* result) { return base::Acquire_Load(result); }
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +TEST(CancelableTask, ManagerBasic) {
|
| + CancelableTaskManager manager;
|
| + manager.CancelAndWait();
|
| +}
|
| +
|
| +
|
| +TEST(CancelableTask, SimpleCancelAndWait) {
|
| + CancelableTaskManager manager;
|
| + ResultType result1 = 0;
|
| + SequentialRunner runner1(
|
| + new MockTask(&manager, &result1, MockTask::kCheckNotRun));
|
| + EXPECT_EQ(GetValue(&result1), 0);
|
| + manager.CancelAndWait();
|
| + EXPECT_EQ(GetValue(&result1), 0);
|
| +}
|
| +
|
| +
|
| +TEST(CancelableTask, SimpleMultipleTasks) {
|
| + CancelableTaskManager manager;
|
| + ResultType result1 = 0;
|
| + ResultType result2 = 0;
|
| + MockTask* task1 = new MockTask(&manager, &result1);
|
| + MockTask* task2 = new MockTask(&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_EQ(manager.Remove(1), false);
|
| + EXPECT_EQ(manager.Remove(2), false);
|
| +}
|
| +
|
| +
|
| +TEST(CancelableTask, ThreadedMultipleTasksStarted) {
|
| + CancelableTaskManager manager;
|
| + ResultType result1 = 0;
|
| + ResultType result2 = 0;
|
| + MockTask* task1 =
|
| + new MockTask(&manager, &result1, MockTask::kWaitTillCanceledAgain);
|
| + MockTask* task2 =
|
| + new MockTask(&manager, &result2, MockTask::kWaitTillCanceledAgain);
|
| + ThreadedRunner runner1(task1);
|
| + ThreadedRunner runner2(task2);
|
| + runner1.Start();
|
| + runner2.Start();
|
| + // Busy wait on result.
|
| + 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;
|
| + MockTask* task1 = new MockTask(&manager, &result1, MockTask::kCheckNotRun);
|
| + MockTask* task2 = new MockTask(&manager, &result2, MockTask::kCheckNotRun);
|
| + ThreadedRunner runner1(task1);
|
| + ThreadedRunner runner2(task2);
|
| + manager.CancelAndWait();
|
| + runner1.Start();
|
| + runner2.Start();
|
| + runner1.Join();
|
| + runner2.Join();
|
| + EXPECT_EQ(GetValue(&result1), 0);
|
| + EXPECT_EQ(GetValue(&result2), 0);
|
| +}
|
| +
|
| +
|
| +TEST(CancelableTask, Cancel) {
|
| + CancelableTaskManager manager;
|
| + ResultType result1 = 0;
|
| + MockTask* task1 = new MockTask(&manager, &result1, MockTask::kCheckNotRun);
|
| + ThreadedRunner runner1(task1);
|
| + uint32_t id = task1->id();
|
| + EXPECT_EQ(id, 1);
|
| + EXPECT_TRUE(manager.Remove(id));
|
| + runner1.Start();
|
| + runner1.Join();
|
| + manager.CancelAndWait();
|
| + EXPECT_EQ(GetValue(&result1), 0);
|
| +}
|
| +
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|