2015-11-14 01:20:07 +00:00
|
|
|
// 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);
|
2015-11-15 12:48:09 +00:00
|
|
|
EXPECT_EQ(task1->id(), 1u);
|
|
|
|
EXPECT_EQ(task2->id(), 2u);
|
2015-11-14 01:20:07 +00:00
|
|
|
|
|
|
|
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);
|
2017-08-02 15:08:48 +00:00
|
|
|
CancelableTaskManager::Id id = task1->id();
|
2015-11-15 12:48:09 +00:00
|
|
|
EXPECT_EQ(id, 1u);
|
2015-11-14 01:20:07 +00:00
|
|
|
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);
|
2017-08-02 15:08:48 +00:00
|
|
|
CancelableTaskManager::Id id = task1->id();
|
2015-11-15 12:48:09 +00:00
|
|
|
EXPECT_EQ(id, 1u);
|
2015-11-14 01:20:07 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2017-01-04 13:45:56 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-11-14 01:20:07 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|