v8/test/unittests/cancelable-tasks-unittest.cc
Michael Lippautz bca8409a39 Make CancelableTask ids unique
They were only limited to 32 bit when using the internal Hashmap. Since
this has changed alreay some time ago, we can switch to 64 bit ids and
check that we never overflow.

Bug: 
Change-Id: Ia6c6d02d6b5e555c6941185a79427dc4aa2a1d62
Reviewed-on: https://chromium-review.googlesource.com/598229
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47085}
2017-08-02 16:10:42 +00:00

264 lines
6.9 KiB
C++

// 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);
CancelableTaskManager::Id 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);
CancelableTaskManager::Id 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));
}
TEST(CancelableTask, EmptyTryAbortAll) {
CancelableTaskManager manager;
EXPECT_EQ(manager.TryAbortAll(), CancelableTaskManager::kTaskRemoved);
}
TEST(CancelableTask, ThreadedMultipleTasksNotRunTryAbortAll) {
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);
EXPECT_EQ(manager.TryAbortAll(), CancelableTaskManager::kTaskAborted);
// 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, ThreadedMultipleTasksStartedTryAbortAll) {
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();
// Busy wait on result to make sure task1 is done.
while (GetValue(&result1) == 0) {
}
EXPECT_EQ(manager.TryAbortAll(), CancelableTaskManager::kTaskRunning);
runner2.Start();
runner1.Join();
runner2.Join();
EXPECT_EQ(GetValue(&result1), 1);
EXPECT_EQ(GetValue(&result2), 0);
}
} // namespace internal
} // namespace v8