普通文本  |  219行  |  5.33 KB

// 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(), 1u);
  EXPECT_EQ(task2->id(), 2u);

  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, 1u);
  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, 1u);
  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