cancelable-tasks-unittest.cc 7.49 KB
Newer Older
1 2 3 4 5 6
// 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"
7
#include "src/tasks/cancelable-task.h"
8
#include "testing/gmock/include/gmock/gmock.h"
9 10 11 12 13 14 15
#include "testing/gtest/include/gtest/gtest.h"

namespace v8 {
namespace internal {

namespace {

16 17
using ResultType = std::atomic<CancelableTaskManager::Id>;

18 19
class CancelableTaskManagerTest;

20 21
class TestTask : public Task, public Cancelable {
 public:
22
  enum Mode { kDoNothing, kWaitTillCancelTriggered, kCheckNotRun };
23

24
  TestTask(CancelableTaskManagerTest* test, ResultType* result, Mode mode);
25

26 27
  // Task override.
  void Run() final;
28 29

 private:
30 31
  ResultType* const result_;
  const Mode mode_;
32
  CancelableTaskManagerTest* const test_;
33 34 35 36
};

class SequentialRunner {
 public:
37 38
  explicit SequentialRunner(std::unique_ptr<TestTask> task)
      : task_(std::move(task)), task_id_(task_->id()) {}
39 40 41

  void Run() {
    task_->Run();
42
    task_.reset();
43 44
  }

45 46
  CancelableTaskManager::Id task_id() const { return task_id_; }

47
 private:
48 49
  std::unique_ptr<TestTask> task_;
  const CancelableTaskManager::Id task_id_;
50 51 52 53
};

class ThreadedRunner final : public base::Thread {
 public:
54 55 56 57
  explicit ThreadedRunner(std::unique_ptr<TestTask> task)
      : Thread(Options("runner thread")),
        task_(std::move(task)),
        task_id_(task_->id()) {}
58

59
  void Run() override {
60
    task_->Run();
61
    task_.reset();
62 63
  }

64 65
  CancelableTaskManager::Id task_id() const { return task_id_; }

66
 private:
67 68
  std::unique_ptr<TestTask> task_;
  const CancelableTaskManager::Id task_id_;
69 70
};

71 72 73
class CancelableTaskManagerTest : public ::testing::Test {
 public:
  CancelableTaskManager* manager() { return &manager_; }
74

75 76
  std::unique_ptr<TestTask> NewTask(
      ResultType* result, TestTask::Mode mode = TestTask::kDoNothing) {
77
    return std::make_unique<TestTask>(this, result, mode);
78 79 80 81 82
  }

  void CancelAndWait() {
    cancel_triggered_.store(true);
    manager_.CancelAndWait();
83
  }
84

85 86 87 88 89 90 91
  TryAbortResult TryAbortAll() {
    cancel_triggered_.store(true);
    return manager_.TryAbortAll();
  }

  bool cancel_triggered() const { return cancel_triggered_.load(); }

92 93
 private:
  CancelableTaskManager manager_;
94
  std::atomic<bool> cancel_triggered_{false};
95
};
96

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
TestTask::TestTask(CancelableTaskManagerTest* test, ResultType* result,
                   Mode mode)
    : Cancelable(test->manager()), result_(result), mode_(mode), test_(test) {}

void TestTask::Run() {
  if (!TryRun()) return;

  result_->store(id());

  switch (mode_) {
    case kWaitTillCancelTriggered:
      // Simple busy wait until the main thread tried to cancel.
      while (!test_->cancel_triggered()) {
      }
      break;
    case kCheckNotRun:
      // Check that we never execute {RunInternal}.
      EXPECT_TRUE(false);
      break;
    default:
      break;
  }
}

121 122
}  // namespace

123
TEST_F(CancelableTaskManagerTest, EmptyCancelableTaskManager) {
124
  CancelAndWait();
125 126
}

127 128 129 130
TEST_F(CancelableTaskManagerTest, SequentialCancelAndWait) {
  ResultType result1{0};
  SequentialRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
  EXPECT_EQ(0u, result1);
131
  CancelAndWait();
132 133 134
  EXPECT_EQ(0u, result1);
  runner1.Run();
  EXPECT_EQ(0u, result1);
135 136
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
TEST_F(CancelableTaskManagerTest, SequentialMultipleTasks) {
  ResultType result1{0};
  ResultType result2{0};
  SequentialRunner runner1(NewTask(&result1));
  SequentialRunner runner2(NewTask(&result2));
  EXPECT_EQ(1u, runner1.task_id());
  EXPECT_EQ(2u, runner2.task_id());

  EXPECT_EQ(0u, result1);
  runner1.Run();
  EXPECT_EQ(1u, result1);

  EXPECT_EQ(0u, result2);
  runner2.Run();
  EXPECT_EQ(2u, result2);

153
  CancelAndWait();
154 155
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(1));
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(2));
156 157
}

158 159 160
TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksStarted) {
  ResultType result1{0};
  ResultType result2{0};
161 162
  ThreadedRunner runner1(NewTask(&result1, TestTask::kWaitTillCancelTriggered));
  ThreadedRunner runner2(NewTask(&result2, TestTask::kWaitTillCancelTriggered));
163 164
  CHECK(runner1.Start());
  CHECK(runner2.Start());
165
  // Busy wait on result to make sure both tasks are done.
166
  while (result1.load() == 0 || result2.load() == 0) {
167
  }
168
  CancelAndWait();
169 170
  runner1.Join();
  runner2.Join();
171 172
  EXPECT_EQ(1u, result1);
  EXPECT_EQ(2u, result2);
173 174
}

175 176 177 178 179
TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksNotRun) {
  ResultType result1{0};
  ResultType result2{0};
  ThreadedRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
  ThreadedRunner runner2(NewTask(&result2, TestTask::kCheckNotRun));
180
  CancelAndWait();
181
  // Tasks are canceled, hence the runner will bail out and not update result.
182 183
  CHECK(runner1.Start());
  CHECK(runner2.Start());
184 185
  runner1.Join();
  runner2.Join();
186 187
  EXPECT_EQ(0u, result1);
  EXPECT_EQ(0u, result2);
188 189
}

190 191 192 193 194 195
TEST_F(CancelableTaskManagerTest, RemoveBeforeCancelAndWait) {
  ResultType result1{0};
  ThreadedRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
  CancelableTaskManager::Id id = runner1.task_id();
  EXPECT_EQ(1u, id);
  EXPECT_EQ(TryAbortResult::kTaskAborted, manager()->TryAbort(id));
196
  CHECK(runner1.Start());
197
  runner1.Join();
198
  CancelAndWait();
199
  EXPECT_EQ(0u, result1);
200 201
}

202 203 204 205 206
TEST_F(CancelableTaskManagerTest, RemoveAfterCancelAndWait) {
  ResultType result1{0};
  ThreadedRunner runner1(NewTask(&result1));
  CancelableTaskManager::Id id = runner1.task_id();
  EXPECT_EQ(1u, id);
207
  CHECK(runner1.Start());
208
  runner1.Join();
209
  CancelAndWait();
210 211
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(id));
  EXPECT_EQ(1u, result1);
212 213
}

214 215 216
TEST_F(CancelableTaskManagerTest, RemoveUnmanagedId) {
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(1));
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(2));
217
  CancelAndWait();
218 219
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(1));
  EXPECT_EQ(TryAbortResult::kTaskRemoved, manager()->TryAbort(3));
220 221
}

222
TEST_F(CancelableTaskManagerTest, EmptyTryAbortAll) {
223
  EXPECT_EQ(TryAbortResult::kTaskRemoved, TryAbortAll());
224
  CancelAndWait();
225 226
}

227 228 229 230 231
TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksNotRunTryAbortAll) {
  ResultType result1{0};
  ResultType result2{0};
  ThreadedRunner runner1(NewTask(&result1, TestTask::kCheckNotRun));
  ThreadedRunner runner2(NewTask(&result2, TestTask::kCheckNotRun));
232
  EXPECT_EQ(TryAbortResult::kTaskAborted, TryAbortAll());
233
  // Tasks are canceled, hence the runner will bail out and not update result.
234 235
  CHECK(runner1.Start());
  CHECK(runner2.Start());
236 237
  runner1.Join();
  runner2.Join();
238 239
  EXPECT_EQ(0u, result1);
  EXPECT_EQ(0u, result2);
240
  CancelAndWait();
241 242
}

243 244 245
TEST_F(CancelableTaskManagerTest, ThreadedMultipleTasksStartedTryAbortAll) {
  ResultType result1{0};
  ResultType result2{0};
246 247
  ThreadedRunner runner1(NewTask(&result1, TestTask::kWaitTillCancelTriggered));
  ThreadedRunner runner2(NewTask(&result2, TestTask::kWaitTillCancelTriggered));
248
  CHECK(runner1.Start());
249
  // Busy wait on result to make sure task1 is done.
250
  while (result1.load() == 0) {
251
  }
252 253 254 255 256 257
  // If the task saw that we triggered the cancel and finished *before* the
  // actual cancel happened, we get {kTaskAborted}. Otherwise, we get
  // {kTaskRunning}.
  EXPECT_THAT(TryAbortAll(),
              testing::AnyOf(testing::Eq(TryAbortResult::kTaskAborted),
                             testing::Eq(TryAbortResult::kTaskRunning)));
258
  CHECK(runner2.Start());
259 260
  runner1.Join();
  runner2.Join();
261 262
  EXPECT_EQ(1u, result1);
  EXPECT_EQ(0u, result2);
263
  CancelAndWait();
264 265
}

266 267
}  // namespace internal
}  // namespace v8