Commit 6a6e23cb authored by Farah Charab's avatar Farah Charab Committed by Peter Beverloo

SQM Fuzzer: Add support for multi-threading.

Adds support to sync the virtual clocks of the various
threads created throughout the porgram. This code doesn't
include cross-thread posting yet.

Bug: 852076
Change-Id: I7936f464f9bfbd35eed3840841c0d8b27c138edc
Reviewed-on: https://chromium-review.googlesource.com/1165145Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584149}
parent 94985647
......@@ -245,6 +245,12 @@ source_set("scheduler_fuzzer_tests") {
"base/sequence_manager_fuzzer_processor.cc",
"base/sequence_manager_fuzzer_processor.h",
"base/sequence_manager_fuzzer_processor_unittest.cc",
"base/simple_thread_impl.cc",
"base/simple_thread_impl.h",
"base/thread_data.cc",
"base/thread_data.h",
"base/thread_pool_manager.cc",
"base/thread_pool_manager.h",
]
deps += [
......@@ -261,6 +267,12 @@ fuzzer_test("sequence_manager_fuzzer") {
"base/sequence_manager_fuzzer.cc",
"base/sequence_manager_fuzzer_processor.cc",
"base/sequence_manager_fuzzer_processor.h",
"base/simple_thread_impl.cc",
"base/simple_thread_impl.h",
"base/thread_data.cc",
"base/thread_data.h",
"base/thread_pool_manager.cc",
"base/thread_pool_manager.h",
]
deps = [
......
......@@ -21,7 +21,7 @@ message SequenceManagerTestDescription {
}
message Action {
// NEXT ID = 11
// NEXT ID = 12
optional uint64 action_id = 1;
......@@ -35,6 +35,7 @@ message SequenceManagerTestDescription {
CreateQueueVoterAction create_queue_voter = 8;
InsertFenceAction insert_fence = 9;
RemoveFenceAction remove_fence = 10;
CreateThreadAction create_thread = 11;
}
}
......@@ -145,5 +146,19 @@ message SequenceManagerTestDescription {
optional uint64 task_queue_id = 2;
}
repeated Action initial_actions = 1;
message CreateThreadAction {
// NEXT ID = 2
repeated Action initial_thread_actions = 1;
}
message InitialThread {
// NEXT ID = 3
optional uint64 action_id = 1;
required CreateThreadAction create_thread = 2;
}
repeated InitialThread main_thread_actions = 1;
}
......@@ -12,9 +12,6 @@
DEFINE_BINARY_PROTO_FUZZER(
const base::sequence_manager::SequenceManagerTestDescription&
fuzzer_input) {
// Dump code for debugging.
// TODO(farahcharab): Add code so that output looks more like the native
// function call.
if (getenv("LPM_DUMP_NATIVE_INPUT")) {
std::cout << fuzzer_input.DebugString() << std::endl;
}
......
#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_fuzzer_processor.h"
#include <algorithm>
#include <string>
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "third_party/blink/renderer/platform/scheduler/base/simple_thread_impl.h"
#include "third_party/blink/renderer/platform/scheduler/base/thread_data.h"
#include "third_party/blink/renderer/platform/scheduler/base/thread_pool_manager.h"
namespace base {
namespace sequence_manager {
......@@ -44,73 +41,126 @@ SequenceManagerFuzzerProcessor::SequenceManagerFuzzerProcessor()
SequenceManagerFuzzerProcessor::SequenceManagerFuzzerProcessor(
bool log_for_testing)
: log_for_testing_(log_for_testing) {
test_task_runner_ = WrapRefCounted(
new TestMockTimeTaskRunner(TestMockTimeTaskRunner::Type::kBoundToThread));
// A zero clock triggers some assertions.
test_task_runner_->AdvanceMockTickClock(TimeDelta::FromMilliseconds(1));
initial_time_ = test_task_runner_->GetMockTickClock()->NowTicks();
: log_for_testing_(log_for_testing),
initial_time_(TimeTicks() + TimeDelta::FromMilliseconds(1)),
thread_pool_manager_(std::make_unique<ThreadPoolManager>(this)),
main_thread_data_(std::make_unique<ThreadData>(initial_time_)) {}
manager_ =
SequenceManagerForTest::Create(nullptr, ThreadTaskRunnerHandle::Get(),
test_task_runner_->GetMockTickClock());
TaskQueue::Spec spec = TaskQueue::Spec("default_task_queue");
task_queues_.emplace_back(manager_->CreateTaskQueue<TestTaskQueue>(spec));
}
SequenceManagerFuzzerProcessor::~SequenceManagerFuzzerProcessor() = default;
void SequenceManagerFuzzerProcessor::RunTest(
const SequenceManagerTestDescription& description) {
for (const auto& initial_action : description.initial_actions()) {
RunAction(initial_action);
DCHECK_CALLED_ON_VALID_THREAD(main_thread_data_->thread_checker_);
for (const auto& initial_action : description.main_thread_actions()) {
ExecuteCreateThreadAction(main_thread_data_.get(),
initial_action.action_id(),
initial_action.create_thread());
}
test_task_runner_->FastForwardUntilNoTasksRemain();
thread_pool_manager_->StartInitialThreads();
thread_pool_manager_->WaitForAllThreads();
if (log_for_testing_) {
ordered_actions_.emplace_back(main_thread_data_->ordered_actions_);
ordered_tasks_.emplace_back(main_thread_data_->ordered_tasks_);
for (auto&& thread : (thread_pool_manager_->threads())) {
DCHECK(thread->thread_data());
ordered_actions_.emplace_back(thread->thread_data()->ordered_actions_);
ordered_tasks_.emplace_back(thread->thread_data()->ordered_tasks_);
}
}
}
void SequenceManagerFuzzerProcessor::RunAction(
ThreadData* thread_data,
const SequenceManagerTestDescription::Action& action) {
if (action.has_create_task_queue()) {
ExecuteCreateTaskQueueAction(action.action_id(),
ExecuteCreateTaskQueueAction(thread_data, action.action_id(),
action.create_task_queue());
} else if (action.has_set_queue_priority()) {
ExecuteSetQueuePriorityAction(action.action_id(),
ExecuteSetQueuePriorityAction(thread_data, action.action_id(),
action.set_queue_priority());
} else if (action.has_set_queue_enabled()) {
ExecuteSetQueueEnabledAction(action.action_id(),
ExecuteSetQueueEnabledAction(thread_data, action.action_id(),
action.set_queue_enabled());
} else if (action.has_create_queue_voter()) {
ExecuteCreateQueueVoterAction(action.action_id(),
ExecuteCreateQueueVoterAction(thread_data, action.action_id(),
action.create_queue_voter());
} else if (action.has_shutdown_task_queue()) {
ExecuteShutdownTaskQueueAction(action.action_id(),
ExecuteShutdownTaskQueueAction(thread_data, action.action_id(),
action.shutdown_task_queue());
} else if (action.has_cancel_task()) {
ExecuteCancelTaskAction(action.action_id(), action.cancel_task());
ExecuteCancelTaskAction(thread_data, action.action_id(),
action.cancel_task());
} else if (action.has_insert_fence()) {
ExecuteInsertFenceAction(action.action_id(), action.insert_fence());
ExecuteInsertFenceAction(thread_data, action.action_id(),
action.insert_fence());
} else if (action.has_remove_fence()) {
ExecuteRemoveFenceAction(action.action_id(), action.remove_fence());
ExecuteRemoveFenceAction(thread_data, action.action_id(),
action.remove_fence());
} else if (action.has_create_thread()) {
ExecuteCreateThreadAction(thread_data, action.action_id(),
action.create_thread());
} else {
ExecutePostDelayedTaskAction(action.action_id(),
ExecutePostDelayedTaskAction(thread_data, action.action_id(),
action.post_delayed_task());
}
}
void SequenceManagerFuzzerProcessor::ExecuteCreateThreadAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CreateThreadAction& action) {
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kCreateThread,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
thread_pool_manager_->CreateThread(
action.initial_thread_actions(),
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
}
void SequenceManagerFuzzerProcessor::ExecuteThread(
ThreadData* thread_data,
const google::protobuf::RepeatedPtrField<
SequenceManagerTestDescription::Action>& initial_thread_actions) {
for (const auto& initial_thread_action : initial_thread_actions) {
RunAction(thread_data, initial_thread_action);
}
while (thread_data->test_task_runner_->GetMockTickClock()->NowTicks() <
TimeTicks::Max()) {
RunLoop().RunUntilIdle();
thread_pool_manager_->AdvanceClockSynchronouslyByPendingTaskDelay(
thread_data);
}
RunLoop().RunUntilIdle();
thread_pool_manager_->ThreadDone();
}
void SequenceManagerFuzzerProcessor::ExecutePostDelayedTaskAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::PostDelayedTaskAction& action) {
DCHECK(!task_queues_.empty());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
LogActionForTesting(action_id, ActionForTest::ActionType::kPostDelayedTask,
test_task_runner_->GetMockTickClock()->NowTicks());
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kPostDelayedTask,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
TestTaskQueue* chosen_task_queue =
thread_data->task_queues_[queue_index].queue.get();
std::unique_ptr<Task> pending_task = std::make_unique<Task>(this);
std::unique_ptr<Task> pending_task =
std::make_unique<Task>(thread_data, this);
// TODO(farahcharab) After adding non-nestable/nestable tasks, fix this to
// PostNonNestableDelayedTask for the former and PostDelayedTask for the
......@@ -121,208 +171,289 @@ void SequenceManagerFuzzerProcessor::ExecutePostDelayedTaskAction(
action.task()),
TimeDelta::FromMilliseconds(action.delay_ms()));
pending_tasks_.push_back(std::move(pending_task));
thread_data->pending_tasks_.push_back(std::move(pending_task));
}
void SequenceManagerFuzzerProcessor::ExecuteCreateTaskQueueAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CreateTaskQueueAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kCreateTaskQueue,
test_task_runner_->GetMockTickClock()->NowTicks());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kCreateTaskQueue,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
TaskQueue::Spec spec = TaskQueue::Spec("test_task_queue");
task_queues_.emplace_back(manager_->CreateTaskQueue<TestTaskQueue>(spec));
task_queues_.back().queue->SetQueuePriority(
thread_data->task_queues_.emplace_back(
thread_data->manager_->CreateTaskQueue<TestTaskQueue>(spec));
thread_data->task_queues_.back().queue->SetQueuePriority(
ToTaskQueuePriority(action.initial_priority()));
}
void SequenceManagerFuzzerProcessor::ExecuteSetQueuePriorityAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::SetQueuePriorityAction& action) {
DCHECK(!task_queues_.empty());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
LogActionForTesting(action_id, ActionForTest::ActionType::kSetQueuePriority,
test_task_runner_->GetMockTickClock()->NowTicks());
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kSetQueuePriority,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
TestTaskQueue* chosen_task_queue =
thread_data->task_queues_[queue_index].queue.get();
chosen_task_queue->SetQueuePriority(ToTaskQueuePriority(action.priority()));
}
void SequenceManagerFuzzerProcessor::ExecuteSetQueueEnabledAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::SetQueueEnabledAction& action) {
DCHECK(!task_queues_.empty());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
LogActionForTesting(action_id, ActionForTest::ActionType::kSetQueueEnabled,
test_task_runner_->GetMockTickClock()->NowTicks());
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kSetQueueEnabled,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
TestTaskQueue* chosen_task_queue =
thread_data->task_queues_[queue_index].queue.get();
if (task_queues_[queue_index].voters.empty()) {
task_queues_[queue_index].voters.push_back(
if (thread_data->task_queues_[queue_index].voters.empty()) {
thread_data->task_queues_[queue_index].voters.push_back(
chosen_task_queue->CreateQueueEnabledVoter());
}
size_t voter_index =
action.voter_id() % task_queues_[queue_index].voters.size();
task_queues_[queue_index].voters[voter_index]->SetQueueEnabled(
action.voter_id() % thread_data->task_queues_[queue_index].voters.size();
thread_data->task_queues_[queue_index].voters[voter_index]->SetQueueEnabled(
action.enabled());
}
void SequenceManagerFuzzerProcessor::ExecuteCreateQueueVoterAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CreateQueueVoterAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kCreateQueueVoter,
test_task_runner_->GetMockTickClock()->NowTicks());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kCreateQueueVoter,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
TestTaskQueue* chosen_task_queue = task_queues_[queue_index].queue.get();
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
TestTaskQueue* chosen_task_queue =
thread_data->task_queues_[queue_index].queue.get();
task_queues_[queue_index].voters.push_back(
thread_data->task_queues_[queue_index].voters.push_back(
chosen_task_queue->CreateQueueEnabledVoter());
}
void SequenceManagerFuzzerProcessor::ExecuteShutdownTaskQueueAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::ShutdownTaskQueueAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kShutdownTaskQueue,
test_task_runner_->GetMockTickClock()->NowTicks());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kShutdownTaskQueue,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
// We always want to have a default task queue.
if (task_queues_.size() > 1) {
size_t queue_index = action.task_queue_id() % task_queues_.size();
task_queues_[queue_index].queue.get()->ShutdownTaskQueue();
task_queues_.erase(task_queues_.begin() + queue_index);
if (thread_data->task_queues_.size() > 1) {
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
thread_data->task_queues_[queue_index].queue.get()->ShutdownTaskQueue();
thread_data->task_queues_.erase(thread_data->task_queues_.begin() +
queue_index);
}
}
void SequenceManagerFuzzerProcessor::ExecuteCancelTaskAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CancelTaskAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kCancelTask,
test_task_runner_->GetMockTickClock()->NowTicks());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kCancelTask,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
if (!pending_tasks_.empty()) {
size_t task_index = action.task_id() % pending_tasks_.size();
pending_tasks_[task_index]->weak_ptr_factory_.InvalidateWeakPtrs();
if (!thread_data->pending_tasks_.empty()) {
size_t task_index = action.task_id() % thread_data->pending_tasks_.size();
thread_data->pending_tasks_[task_index]
->weak_ptr_factory_.InvalidateWeakPtrs();
// If it is already running, it is a parent task and will be deleted when
// it is done.
if (!pending_tasks_[task_index]->is_running) {
pending_tasks_.erase(pending_tasks_.begin() + task_index);
if (!thread_data->pending_tasks_[task_index]->is_running) {
thread_data->pending_tasks_.erase(thread_data->pending_tasks_.begin() +
task_index);
}
}
}
void SequenceManagerFuzzerProcessor::ExecuteInsertFenceAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::InsertFenceAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kInsertFence,
test_task_runner_->GetMockTickClock()->NowTicks());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
size_t queue_index = action.task_queue_id() % task_queues_.size();
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kInsertFence,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
if (action.position() ==
SequenceManagerTestDescription::InsertFenceAction::NOW) {
task_queues_[queue_index].queue.get()->InsertFence(
thread_data->task_queues_[queue_index].queue.get()->InsertFence(
TaskQueue::InsertFencePosition::kNow);
} else {
task_queues_[queue_index].queue.get()->InsertFence(
thread_data->task_queues_[queue_index].queue.get()->InsertFence(
TaskQueue::InsertFencePosition::kBeginningOfTime);
}
}
void SequenceManagerFuzzerProcessor::ExecuteRemoveFenceAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::RemoveFenceAction& action) {
LogActionForTesting(action_id, ActionForTest::ActionType::kRemoveFence,
test_task_runner_->GetMockTickClock()->NowTicks());
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
DCHECK(!thread_data->task_queues_.empty());
LogActionForTesting(
thread_data, action_id, ActionForTest::ActionType::kRemoveFence,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
size_t queue_index = action.task_queue_id() % task_queues_.size();
task_queues_[queue_index].queue.get()->RemoveFence();
size_t queue_index =
action.task_queue_id() % thread_data->task_queues_.size();
thread_data->task_queues_[queue_index].queue.get()->RemoveFence();
}
void SequenceManagerFuzzerProcessor::ExecuteTask(
ThreadData* thread_data,
const SequenceManagerTestDescription::Task& task) {
TimeTicks start_time = test_task_runner_->GetMockTickClock()->NowTicks();
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
TimeTicks start_time =
thread_data->test_task_runner_->GetMockTickClock()->NowTicks();
// We can limit the depth of the nested post delayed action when processing
// the proto.
for (const auto& task_action : task.actions()) {
// TODO(farahcharab) Add run loop to deal with nested tasks later. So far,
// we are assuming tasks are non-nestable.
RunAction(task_action);
RunAction(thread_data, task_action);
}
TimeTicks end_time = test_task_runner_->GetMockTickClock()->NowTicks();
TimeTicks end_time =
thread_data->test_task_runner_->GetMockTickClock()->NowTicks();
test_task_runner_->AdvanceMockTickClock(
TimeDelta::FromMilliseconds(task.duration_ms()) -
TimeTicks next_time =
start_time +
std::max(TimeDelta(), TimeDelta::FromMilliseconds(task.duration_ms()) -
(end_time - start_time));
LogTaskForTesting(task.task_id(), start_time,
test_task_runner_->GetMockTickClock()->NowTicks());
while (thread_data->test_task_runner_->GetMockTickClock()->NowTicks() !=
next_time) {
thread_pool_manager_->AdvanceClockSynchronouslyToTime(thread_data,
next_time);
}
LogTaskForTesting(
thread_data, task.task_id(), start_time,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
}
void SequenceManagerFuzzerProcessor::DeleteTask(Task* task) {
DCHECK_CALLED_ON_VALID_THREAD(task->thread_data_->thread_checker_);
size_t i = 0;
while (i < pending_tasks_.size() && task != pending_tasks_[i].get()) {
std::vector<std::unique_ptr<Task>>* pending_tasks =
&(task->thread_data_->pending_tasks_);
while (i < pending_tasks->size() && task != (*pending_tasks)[i].get()) {
i++;
}
pending_tasks_.erase(pending_tasks_.begin() + i);
pending_tasks->erase(pending_tasks->begin() + i);
}
void SequenceManagerFuzzerProcessor::LogTaskForTesting(uint64_t task_id,
void SequenceManagerFuzzerProcessor::LogTaskForTesting(ThreadData* thread_data,
uint64_t task_id,
TimeTicks start_time,
TimeTicks end_time) {
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
if (!log_for_testing_)
return;
uint64_t start_time_ms = (start_time - initial_time_).InMilliseconds();
uint64_t end_time_ms = (end_time - initial_time_).InMilliseconds();
ordered_tasks_.emplace_back(task_id, start_time_ms, end_time_ms);
thread_data->ordered_tasks_.emplace_back(task_id, start_time_ms, end_time_ms);
}
void SequenceManagerFuzzerProcessor::LogActionForTesting(
ThreadData* thread_data,
uint64_t action_id,
ActionForTest::ActionType type,
TimeTicks start_time) {
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
if (!log_for_testing_)
return;
ordered_actions_.emplace_back(action_id, type,
(start_time - initial_time_).InMilliseconds());
thread_data->ordered_actions_.emplace_back(
action_id, type, (start_time - initial_time_).InMilliseconds());
}
const std::vector<SequenceManagerFuzzerProcessor::TaskForTest>&
const std::vector<std::vector<SequenceManagerFuzzerProcessor::TaskForTest>>&
SequenceManagerFuzzerProcessor::ordered_tasks() const {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_data_->thread_checker_);
return ordered_tasks_;
}
const std::vector<SequenceManagerFuzzerProcessor::ActionForTest>&
const std::vector<std::vector<SequenceManagerFuzzerProcessor::ActionForTest>>&
SequenceManagerFuzzerProcessor::ordered_actions() const {
DCHECK_CALLED_ON_VALID_THREAD(main_thread_data_->thread_checker_);
return ordered_actions_;
}
SequenceManagerFuzzerProcessor::Task::Task(
ThreadData* thread_data,
SequenceManagerFuzzerProcessor* processor)
: is_running(false), processor_(processor), weak_ptr_factory_(this) {}
: is_running(false),
processor_(processor),
thread_data_(thread_data),
weak_ptr_factory_(this) {
DCHECK(processor_);
DCHECK(thread_data_);
}
void SequenceManagerFuzzerProcessor::Task::Execute(
const SequenceManagerTestDescription::Task& task) {
DCHECK_CALLED_ON_VALID_THREAD(thread_data_->thread_checker_);
is_running = true;
processor_->ExecuteTask(task);
processor_->ExecuteTask(thread_data_, task);
processor_->DeleteTask(this);
}
SequenceManagerFuzzerProcessor::TaskForTest::TaskForTest(uint64_t id,
uint64_t start,
uint64_t end)
: task_id(id), start_time_ms(start), end_time_ms(end) {}
uint64_t start_time_ms,
uint64_t end_time_ms)
: task_id(id), start_time_ms(start_time_ms), end_time_ms(end_time_ms) {}
bool SequenceManagerFuzzerProcessor::TaskForTest::operator==(
const TaskForTest& rhs) const {
......@@ -330,10 +461,11 @@ bool SequenceManagerFuzzerProcessor::TaskForTest::operator==(
end_time_ms == rhs.end_time_ms;
}
SequenceManagerFuzzerProcessor::ActionForTest::ActionForTest(uint64_t id,
SequenceManagerFuzzerProcessor::ActionForTest::ActionForTest(
uint64_t id,
ActionType type,
uint64_t start)
: action_id(id), action_type(type), start_time_ms(start) {}
uint64_t start_time_ms)
: action_id(id), action_type(type), start_time_ms(start_time_ms) {}
bool SequenceManagerFuzzerProcessor::ActionForTest::operator==(
const ActionForTest& rhs) const {
......
......@@ -5,14 +5,10 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_FUZZER_PROCESSOR_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SEQUENCE_MANAGER_FUZZER_PROCESSOR_H_
#include <memory>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/task/sequence_manager/test/test_task_queue.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/time/time.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/base/proto/sequence_manager_test_description.pb.h"
......@@ -20,8 +16,15 @@
namespace base {
namespace sequence_manager {
class ThreadData;
class ThreadPoolManager;
// Provides functionality to parse the fuzzer's test description and run the
// relevant APIs.
//
// Warning: For unit testing purposes, the thread data of the threads managed by
// the |thread_pool_manager_| should live for the scope of the main thread
// entry function i.e RunTest.
class PLATFORM_EXPORT SequenceManagerFuzzerProcessor {
public:
// Public interface used to parse the fuzzer's test description and
......@@ -30,7 +33,7 @@ class PLATFORM_EXPORT SequenceManagerFuzzerProcessor {
protected:
struct TaskForTest {
TaskForTest(uint64_t id, uint64_t start, uint64_t end);
TaskForTest(uint64_t id, uint64_t start_time_ms, uint64_t end_time_ms);
bool operator==(const TaskForTest& rhs) const;
uint64_t task_id;
......@@ -48,10 +51,12 @@ class PLATFORM_EXPORT SequenceManagerFuzzerProcessor {
kCancelTask,
kShutdownTaskQueue,
kInsertFence,
kRemoveFence
kRemoveFence,
kCreateThread,
};
ActionForTest(uint64_t id, ActionType type, uint64_t start);
ActionForTest(uint64_t id, ActionType type, uint64_t start_time_ms);
bool operator==(const ActionForTest& rhs) const;
uint64_t action_id;
......@@ -59,102 +64,128 @@ class PLATFORM_EXPORT SequenceManagerFuzzerProcessor {
uint64_t start_time_ms;
};
SequenceManagerFuzzerProcessor();
explicit SequenceManagerFuzzerProcessor(bool log_for_testing);
void RunTest(const SequenceManagerTestDescription& description);
void RunAction(const SequenceManagerTestDescription::Action& action);
const std::vector<TaskForTest>& ordered_tasks() const;
const std::vector<ActionForTest>& ordered_actions() const;
private:
class Task {
public:
Task(SequenceManagerFuzzerProcessor* processor);
Task(ThreadData* thread_data, SequenceManagerFuzzerProcessor* processor);
~Task() = default;
void Execute(const SequenceManagerTestDescription::Task& task);
bool is_running;
SequenceManagerFuzzerProcessor* processor_;
ThreadData* thread_data_;
base::WeakPtrFactory<Task> weak_ptr_factory_;
};
struct TaskQueueWithVoters {
TaskQueueWithVoters(scoped_refptr<TestTaskQueue> task_queue)
: queue(std::move(task_queue)){};
SequenceManagerFuzzerProcessor();
explicit SequenceManagerFuzzerProcessor(bool log_for_testing);
scoped_refptr<TestTaskQueue> queue;
std::vector<std::unique_ptr<TaskQueue::QueueEnabledVoter>> voters;
};
~SequenceManagerFuzzerProcessor();
void RunTest(const SequenceManagerTestDescription& description);
void ExecuteThread(
ThreadData* thread_data,
const google::protobuf::RepeatedPtrField<
SequenceManagerTestDescription::Action>& initial_thread_actions);
// Returns an ordered list of tasks executed on each thread. Note that the
// ordering of the threads isn't deterministic since it follows the order in
// which the threads were constructed. Furthermore, given that
// ThreadPoolManager::CreateThread is used to construct these threads and
// given that it can be called from multiple threads, the order of
// construction isn't deterministic.
const std::vector<std::vector<TaskForTest>>& ordered_tasks() const;
// Returns an ordered list of actions executed on each thread. Note that the
// ordering of the threads isn't deterministic. For more details, check the
// comment above on ordered_tasks().
const std::vector<std::vector<ActionForTest>>& ordered_actions() const;
private:
friend class ThreadData;
friend class ThreadPoolManager;
void RunAction(ThreadData* thread_data,
const SequenceManagerTestDescription::Action& action);
void ExecuteCreateThreadAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CreateThreadAction& action);
void ExecuteCreateTaskQueueAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CreateTaskQueueAction& action);
void ExecutePostDelayedTaskAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::PostDelayedTaskAction& action);
void ExecuteSetQueuePriorityAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::SetQueuePriorityAction& action);
void ExecuteSetQueueEnabledAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::SetQueueEnabledAction& action);
void ExecuteCreateQueueVoterAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CreateQueueVoterAction& action);
void ExecuteShutdownTaskQueueAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::ShutdownTaskQueueAction& action);
void ExecuteCancelTaskAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::CancelTaskAction& action);
void ExecuteInsertFenceAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::InsertFenceAction& action);
void ExecuteRemoveFenceAction(
ThreadData* thread_data,
uint64_t action_id,
const SequenceManagerTestDescription::RemoveFenceAction& action);
void ExecuteTask(const SequenceManagerTestDescription::Task& task);
void ExecuteTask(ThreadData* thread_data,
const SequenceManagerTestDescription::Task& task);
void DeleteTask(Task* task);
void LogTaskForTesting(uint64_t task_id,
void LogTaskForTesting(ThreadData* thread_data,
uint64_t task_id,
TimeTicks start_time,
TimeTicks end_time);
void LogActionForTesting(uint64_t action_id,
void LogActionForTesting(ThreadData* thread_data,
uint64_t action_id,
ActionForTest::ActionType type,
TimeTicks start_time);
// Bound to current thread. Used to control the clock of the task queue
// manager.
scoped_refptr<TestMockTimeTaskRunner> test_task_runner_;
std::unique_ptr<SequenceManagerForTest> manager_;
// For testing purposes, this should follow the order in which the queues were
// created.
std::vector<TaskQueueWithVoters> task_queues_;
// Used to be able to cancel pending tasks from the sequence manager. For
// testing purposes, this should follow the order in which the tasks were
// posted.
std::vector<std::unique_ptr<Task>> pending_tasks_;
const bool log_for_testing_;
TimeTicks initial_time_;
// For Testing. Used to log tasks in their order of execution.
std::vector<TaskForTest> ordered_tasks_;
std::unique_ptr<ThreadPoolManager> thread_pool_manager_;
// The clock of the main thread data task runner is initialized to
// |initial_time_| and never advanced, since it can only execute actions at
// the start of the program.
std::unique_ptr<ThreadData> main_thread_data_;
// For Testing. Each entry contains the ordered list of tasks for one of the
// created threads. The first entry is reserved for the main thread (which is
// always empty since no tasks are executed on the main thread).
std::vector<std::vector<TaskForTest>> ordered_tasks_;
// For Testing. Used to log actions in their order of execution.
std::vector<ActionForTest> ordered_actions_;
// For Testing. Each entry contains the ordered list of actions for one of the
// created threads. The first entry is reserved for the main thread (which
// can only contain ActionType::kCreateThread actions).
std::vector<std::vector<ActionForTest>> ordered_actions_;
};
} // namespace sequence_manager
......
......@@ -6,6 +6,7 @@
#include <memory>
#include "base/strings/strcat.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/scheduler/base/proto/sequence_manager_test_description.pb.h"
......@@ -17,6 +18,7 @@ namespace sequence_manager {
using testing::ContainerEq;
using testing::IsEmpty;
using testing::UnorderedElementsAreArray;
class SequenceManagerFuzzerProcessorForTest
: public SequenceManagerFuzzerProcessor {
......@@ -24,9 +26,10 @@ class SequenceManagerFuzzerProcessorForTest
SequenceManagerFuzzerProcessorForTest()
: SequenceManagerFuzzerProcessor(true) {}
static void ParseAndRun(std::string test_description,
std::vector<TaskForTest>* executed_tasks,
std::vector<ActionForTest>* executed_actions) {
static void ParseAndRun(
std::string test_description,
std::vector<std::vector<TaskForTest>>* executed_tasks,
std::vector<std::vector<ActionForTest>>* executed_actions) {
SequenceManagerTestDescription proto_description;
google::protobuf::TextFormat::ParseFromString(test_description,
&proto_description);
......@@ -40,6 +43,26 @@ class SequenceManagerFuzzerProcessorForTest
*executed_actions = processor.ordered_actions();
}
static void ParseAndRunSingleThread(
std::string test_description,
std::vector<TaskForTest>* executed_tasks,
std::vector<ActionForTest>* executed_actions) {
SequenceManagerTestDescription proto_description;
google::protobuf::TextFormat::ParseFromString(
base::StrCat(
{"main_thread_actions { create_thread {", test_description, "}}"}),
&proto_description);
SequenceManagerFuzzerProcessorForTest processor;
processor.RunTest(proto_description);
if (executed_tasks)
*executed_tasks = processor.ordered_tasks()[1];
if (executed_actions)
*executed_actions = processor.ordered_actions()[1];
}
using SequenceManagerFuzzerProcessor::ordered_actions;
using SequenceManagerFuzzerProcessor::ordered_tasks;
......@@ -55,13 +78,14 @@ TEST(SequenceManagerFuzzerProcessorTest, CreateTaskQueue) {
// Describes a test that creates a task queue and posts a task to create a
// task queue.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
......@@ -74,8 +98,7 @@ TEST(SequenceManagerFuzzerProcessorTest, CreateTaskQueue) {
}
}
})",
nullptr,
&executed_actions);
nullptr, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
......@@ -92,14 +115,15 @@ TEST(SequenceManagerFuzzerProcessorTest, CreateQueueVoter) {
// Describes a test that creates a voter and posts a task to create a queue
// voter.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_queue_voter {
task_queue_id : 1
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
......@@ -112,8 +136,7 @@ TEST(SequenceManagerFuzzerProcessorTest, CreateQueueVoter) {
}
}
})",
nullptr,
&executed_actions);
nullptr, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateQueueVoter,
......@@ -130,8 +153,9 @@ TEST(SequenceManagerFuzzerProcessorTest, PostDelayedTaskWithDuration) {
std::vector<ActionForTest> executed_actions;
// Posts an 10 ms delayed task of duration 20 ms.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task_queue_id : 1
......@@ -142,8 +166,7 @@ TEST(SequenceManagerFuzzerProcessorTest, PostDelayedTaskWithDuration) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 10, 30);
......@@ -160,15 +183,16 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueuePriority) {
// Describes a test that sets the priority of queue and posts a task to set
// the priority of a queue.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
set_queue_priority {
task_queue_id: 2
priority: CONTROL
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
......@@ -183,8 +207,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueuePriority) {
}
}
})",
nullptr,
&executed_actions);
nullptr, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kSetQueuePriority,
......@@ -203,8 +226,9 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabled) {
// Describes a test that posts a number of tasks to a certain queue, disable
// that queue, and post some more tasks to the same queue.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task_queue_id: 1
......@@ -213,7 +237,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabled) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 10
......@@ -223,14 +247,14 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabled) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 3
set_queue_enabled {
task_queue_id: 1
enabled: false
}
}
initial_actions {
initial_thread_actions {
action_id : 4
post_delayed_task {
task_queue_id: 1
......@@ -239,8 +263,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabled) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
......@@ -266,13 +289,14 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabledWithDelays) {
// 10ms and 20ms, respectively; and other no-op tasks in the different
// intervals to verify that the queue is indeed being disabled/enabled
// properly.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
task_queue_id: 1
......@@ -281,7 +305,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabledWithDelays) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 3
post_delayed_task {
delay_ms : 15
......@@ -291,7 +315,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabledWithDelays) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 10
......@@ -308,7 +332,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabledWithDelays) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 20
......@@ -325,7 +349,7 @@ TEST(SequenceManagerFuzzerProcessorTest, SetQueueEnabledWithDelays) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 8
post_delayed_task {
task_queue_id: 1
......@@ -361,20 +385,21 @@ TEST(SequenceManagerFuzzerProcessorTest, MultipleVoters) {
// Describes a test that creates two voters for a queue, where one voter
// enables the queue, and the other disables it.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_queue_voter {
task_queue_id : 1
}
}
initial_actions {
initial_thread_actions {
action_id : 2
create_queue_voter {
task_queue_id : 1
}
}
initial_actions {
initial_thread_actions {
action_id : 3
set_queue_enabled {
voter_id : 1
......@@ -382,7 +407,7 @@ TEST(SequenceManagerFuzzerProcessorTest, MultipleVoters) {
enabled : true
}
}
initial_actions {
initial_thread_actions {
action_id : 4
set_queue_enabled {
voter_id : 2
......@@ -390,7 +415,7 @@ TEST(SequenceManagerFuzzerProcessorTest, MultipleVoters) {
enabled : false
}
}
initial_actions {
initial_thread_actions {
action_id : 5
post_delayed_task {
task_queue_id: 1
......@@ -399,8 +424,7 @@ TEST(SequenceManagerFuzzerProcessorTest, MultipleVoters) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateQueueVoter,
......@@ -426,13 +450,14 @@ TEST(SequenceManagerFuzzerProcessorTest, ShutdownTaskQueue) {
std::vector<ActionForTest> executed_actions;
std::vector<TaskForTest> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
task_queue_id: 1
......@@ -441,7 +466,7 @@ TEST(SequenceManagerFuzzerProcessorTest, ShutdownTaskQueue) {
}
}
}
initial_actions {
initial_thread_actions {
action_id :3
post_delayed_task {
delay_ms : 10
......@@ -451,7 +476,7 @@ TEST(SequenceManagerFuzzerProcessorTest, ShutdownTaskQueue) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 4
post_delayed_task {
task_queue_id: 0
......@@ -461,13 +486,13 @@ TEST(SequenceManagerFuzzerProcessorTest, ShutdownTaskQueue) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 5
shutdown_task_queue {
task_queue_id: 1
}
}
initial_actions {
initial_thread_actions {
action_id : 6
post_delayed_task {
task_queue_id: 1
......@@ -476,8 +501,7 @@ TEST(SequenceManagerFuzzerProcessorTest, ShutdownTaskQueue) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
......@@ -510,8 +534,9 @@ TEST(SequenceManagerFuzzerProcessorTest,
ShutdownTaskQueueWhenOneQueueAvailable) {
std::vector<TaskForTest> executed_tasks;
std::vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task {
......@@ -519,14 +544,13 @@ TEST(SequenceManagerFuzzerProcessorTest,
}
}
}
initial_actions {
initial_thread_actions {
action_id : 2
shutdown_task_queue {
task_queue_id: 1
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
......@@ -538,7 +562,7 @@ TEST(SequenceManagerFuzzerProcessorTest,
std::vector<TaskForTest> expected_tasks;
// We always want to have a default task queue in the fuzzer processor. So, if
// We always want to have a default task queue in every thread. So, if
// we have only one queue, the shutdown action is effectively a no-op.
expected_tasks.emplace_back(1, 0, 0);
......@@ -548,13 +572,14 @@ TEST(SequenceManagerFuzzerProcessorTest,
TEST(SequenceManagerFuzzerProcessorTest, ShutdownPostingTaskQueue) {
std::vector<TaskForTest> executed_tasks;
std::vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
task_queue_id : 0
......@@ -569,8 +594,7 @@ TEST(SequenceManagerFuzzerProcessorTest, ShutdownPostingTaskQueue) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
......@@ -592,8 +616,9 @@ TEST(SequenceManagerFuzzerProcessorTest, CancelParentTask) {
std::vector<ActionForTest> executed_actions;
std::vector<TaskForTest> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task {
......@@ -623,8 +648,7 @@ TEST(SequenceManagerFuzzerProcessorTest, CancelParentTask) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
......@@ -651,8 +675,9 @@ TEST(SequenceManagerFuzzerProcessorTest, CancelTask) {
std::vector<TaskForTest> executed_tasks;
std::vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
task {
......@@ -660,15 +685,14 @@ TEST(SequenceManagerFuzzerProcessorTest, CancelTask) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 2
cancel_task {
task_id : 1
}
}
)",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kPostDelayedTask,
......@@ -682,16 +706,16 @@ TEST(SequenceManagerFuzzerProcessorTest, CancelTask) {
TEST(SequenceManagerFuzzerProcessorTest, CancelTaskWhenNoneArePending) {
std::vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
cancel_task {
task_id : 1
}
}
)",
nullptr,
&executed_actions);
nullptr, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCancelTask, 0);
......@@ -704,8 +728,9 @@ TEST(SequenceManagerFuzzerProcessorTest,
std::vector<ActionForTest> executed_actions;
// Posts a task of duration 40 ms and a 10 ms delayed task of duration 20 ms.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
delay_ms : 10
......@@ -715,7 +740,7 @@ TEST(SequenceManagerFuzzerProcessorTest,
}
}
}
initial_actions {
initial_thread_actions {
action_id :2
post_delayed_task {
delay_ms : 0
......@@ -725,12 +750,12 @@ TEST(SequenceManagerFuzzerProcessorTest,
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<TaskForTest> expected_tasks;
// Task with id 2 is expected to run first and block the other task until it
// done.
std::vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(2, 0, 40);
expected_tasks.emplace_back(1, 40, 60);
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
......@@ -745,11 +770,13 @@ TEST(SequenceManagerFuzzerProcessorTest,
TEST(SequenceManagerFuzzerProcessorTest,
TaskDurationBlocksOtherNonNestableTaskWhenPostedFromTheWithinTask) {
std::vector<TaskForTest> executed_tasks;
// Posts an instant task of duration 40 ms that posts another non-nested
// instant task.
std::vector<TaskForTest> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
post_delayed_task {
task {
task_id : 1
......@@ -766,11 +793,12 @@ TEST(SequenceManagerFuzzerProcessorTest,
})",
&executed_tasks, nullptr);
std::vector<TaskForTest> expected_tasks;
// Task with task id 1 is expected to run for 40 ms, and block the other
// posted task from running until its done. Note that the task with id 2 is
// blocked since it is non-nested, so it is not supposed to run from within
// the posting task.
std::vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 0, 40);
expected_tasks.emplace_back(2, 40, 40);
......@@ -778,14 +806,15 @@ TEST(SequenceManagerFuzzerProcessorTest,
}
TEST(SequenceManagerFuzzerProcessorTest, PostNonEmptyTask) {
// Posts a 5 ms delayed task of duration 40 ms that creates a task queue,
// posts a 4 ms delayed task, posts an instant task, creates a task queue,
// and then posts a 40 ms delayed task.
std::vector<TaskForTest> executed_tasks;
std::vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
// Posts a 5 ms delayed task of duration 40 ms that creates a task queue,
// posts a 4 ms delayed task, posts an instant task, creates a task queue,
// and then posts a 40 ms delayed task.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
post_delayed_task {
delay_ms: 5
......@@ -831,14 +860,14 @@ TEST(SequenceManagerFuzzerProcessorTest, PostNonEmptyTask) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<TaskForTest> expected_tasks;
// Task with task id 1 is expected to run first, and block all other pending
// tasks until its done. The remaining tasks will be executed in
// non-decreasing order of the delay parameter with ties broken by
// the post order.
std::vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 5, 45);
expected_tasks.emplace_back(3, 45, 45);
expected_tasks.emplace_back(2, 45, 45);
......@@ -862,18 +891,19 @@ TEST(SequenceManagerFuzzerProcessorTest, PostNonEmptyTask) {
}
TEST(SequenceManagerFuzzerProcessorTest, OrderOfSimpleUnnestedExecutedActions) {
// Creates a task queue, creates a task queue after 10 ms of delay, posts a
// task after 20 ms delay, posts a 10 ms duration task after 15 ms of delay,
// and posts a task after 100 ms of delay.
std::vector<TaskForTest> executed_tasks;
std::vector<ActionForTest> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
// Creates a task queue, posts a task after 20 ms delay, posts a 10 ms
// duration task after 15 ms of delay, and posts a task after 100 ms of delay.
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 10
......@@ -887,7 +917,7 @@ TEST(SequenceManagerFuzzerProcessorTest, OrderOfSimpleUnnestedExecutedActions) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 20
......@@ -896,7 +926,7 @@ TEST(SequenceManagerFuzzerProcessorTest, OrderOfSimpleUnnestedExecutedActions) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 5
post_delayed_task {
delay_ms : 15
......@@ -906,7 +936,7 @@ TEST(SequenceManagerFuzzerProcessorTest, OrderOfSimpleUnnestedExecutedActions) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 100
......@@ -915,13 +945,13 @@ TEST(SequenceManagerFuzzerProcessorTest, OrderOfSimpleUnnestedExecutedActions) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<TaskForTest> expected_tasks;
// Tasks are expected to run in order of non-decreasing delay with ties broken
// by order of posting. Note that the task with id 3 will block the task with
// id 2 from running at its scheduled time.
std::vector<TaskForTest> expected_tasks;
expected_tasks.emplace_back(1, 10, 10);
expected_tasks.emplace_back(3, 15, 25);
expected_tasks.emplace_back(2, 25, 25);
......@@ -951,13 +981,14 @@ TEST(SequenceManagerFuzzerProcessorTest, InsertAndRemoveFence) {
// Describes a test that inserts a fence to a task queue after a delay of
// 20ms, posts a task to it after a delay of 25ms, and removes the fence after
// a delay of 30ms.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
create_task_queue{
}
}
initial_actions {
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 20
......@@ -974,7 +1005,7 @@ TEST(SequenceManagerFuzzerProcessorTest, InsertAndRemoveFence) {
}
}
}
initial_actions {
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 30
......@@ -990,7 +1021,7 @@ TEST(SequenceManagerFuzzerProcessorTest, InsertAndRemoveFence) {
}
}
}
initial_actions {
initial_thread_actions {
action_id: 6
post_delayed_task {
delay_ms: 25
......@@ -1000,8 +1031,7 @@ TEST(SequenceManagerFuzzerProcessorTest, InsertAndRemoveFence) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kCreateTaskQueue,
......@@ -1033,15 +1063,16 @@ TEST(SequenceManagerFuzzerProcessorTest, ThrottleTaskQueue) {
std::vector<TaskForTest> executed_tasks;
// Describes a test that throttles a task queue, and posts a task to it.
SequenceManagerFuzzerProcessorForTest::ParseAndRun(R"(
initial_actions {
SequenceManagerFuzzerProcessorForTest::ParseAndRunSingleThread(
R"(
initial_thread_actions {
action_id : 1
insert_fence {
position: BEGINNING_OF_TIME
task_queue_id: 1
}
}
initial_actions {
initial_thread_actions {
action_id: 2
post_delayed_task {
task_queue_id: 1
......@@ -1050,8 +1081,7 @@ TEST(SequenceManagerFuzzerProcessorTest, ThrottleTaskQueue) {
}
}
})",
&executed_tasks,
&executed_actions);
&executed_tasks, &executed_actions);
std::vector<ActionForTest> expected_actions;
expected_actions.emplace_back(1, ActionForTest::ActionType::kInsertFence, 0);
......@@ -1064,5 +1094,227 @@ TEST(SequenceManagerFuzzerProcessorTest, ThrottleTaskQueue) {
EXPECT_THAT(executed_tasks, IsEmpty());
}
TEST(SequenceManagerFuzzerProcessorTest, MultipleThreadsButNotInteracting) {
std::string thread_actions =
R"(
main_thread_actions {
action_id : 1
create_thread {
initial_thread_actions {
action_id : 1
create_task_queue {
}
}
initial_thread_actions {
action_id : 2
post_delayed_task {
delay_ms : 10
task {
task_id : 1
actions {
action_id : 3
create_task_queue {
}
}
}
}
}
initial_thread_actions {
action_id : 4
post_delayed_task {
delay_ms : 20
task {
task_id : 2
}
}
}
initial_thread_actions {
action_id : 5
post_delayed_task {
delay_ms : 15
task {
task_id : 3
duration_ms : 10
}
}
}
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 100
task {
task_id : 4
}
}
}
}
})";
// Threads initialized with same list of actions.
std::vector<std::string> threads{thread_actions, thread_actions,
thread_actions, thread_actions,
thread_actions};
std::vector<std::vector<ActionForTest>> executed_actions;
std::vector<std::vector<TaskForTest>> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
base::StrCat(threads), &executed_tasks, &executed_actions);
// |expected_tasks[0]| is empty since the main thread doesn't execute any
// task.
std::vector<std::vector<TaskForTest>> expected_tasks(6);
for (int i = 1; i <= 5; i++) {
// Created thread tasks: tasks are expected to run in order of
// non-decreasing delay with ties broken by order of posting. Note that the
// task with id 3 will block the task with id 2 from running at its
// scheduled time.
expected_tasks[i].emplace_back(1, 10, 10);
expected_tasks[i].emplace_back(3, 15, 25);
expected_tasks[i].emplace_back(2, 25, 25);
expected_tasks[i].emplace_back(4, 100, 100);
}
EXPECT_THAT(executed_tasks, ContainerEq(expected_tasks));
std::vector<std::vector<ActionForTest>> expected_actions(6);
for (int i = 1; i <= 5; i++) {
// Main thread action: creating the Ith thread.
expected_actions[0].emplace_back(
1, ActionForTest::ActionType::kCreateThread, 0);
// Actions of the Ith thread.
expected_actions[i].emplace_back(
1, ActionForTest::ActionType::kCreateTaskQueue, 0);
expected_actions[i].emplace_back(
2, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
4, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
5, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
6, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[i].emplace_back(
3, ActionForTest::ActionType::kCreateTaskQueue, 10);
}
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, CreateThreadRecursively) {
std::vector<std::vector<ActionForTest>> executed_actions;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
action_id : 1
create_thread {
initial_thread_actions {
action_id : 2
create_thread {
initial_thread_actions {
action_id : 3
create_thread {}
}
}
}
}
}
)",
nullptr, &executed_actions);
// Last thread has no actions, so |expected_actions[3]| is empty.
std::vector<std::vector<ActionForTest>> expected_actions(4);
for (int i = 0; i <= 2; i++) {
// Actions of the Ith thread.
expected_actions[i].emplace_back(
i + 1, ActionForTest::ActionType::kCreateThread, 0);
}
EXPECT_THAT(executed_actions, ContainerEq(expected_actions));
}
TEST(SequenceManagerFuzzerProcessorTest, PostTaskToCreateThread) {
std::vector<std::vector<ActionForTest>> executed_actions;
std::vector<std::vector<TaskForTest>> executed_tasks;
SequenceManagerFuzzerProcessorForTest::ParseAndRun(
R"(
main_thread_actions {
action_id : 1
create_thread {
initial_thread_actions {
action_id : 2
post_delayed_task {
task {
task_id: 1
actions {
action_id : 3
create_thread {
}
}
}
}
}
initial_thread_actions {
action_id : 4
create_thread {
}
}
}
}
main_thread_actions {
action_id : 5
create_thread {
initial_thread_actions {
action_id : 6
post_delayed_task {
delay_ms : 20
task {
task_id: 2
duration_ms : 30
actions {
action_id : 7
create_thread {
}
}
}
}
}
}
})",
&executed_tasks, &executed_actions);
// Third, Fourth and Fifth created threads execute no actions.
std::vector<std::vector<ActionForTest>> expected_actions(6);
expected_actions[0].emplace_back(1, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[0].emplace_back(5, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[1].emplace_back(
2, ActionForTest::ActionType::kPostDelayedTask, 0);
// Posted messages execute after instant actions.
expected_actions[1].emplace_back(4, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[1].emplace_back(3, ActionForTest::ActionType::kCreateThread,
0);
expected_actions[2].emplace_back(
6, ActionForTest::ActionType::kPostDelayedTask, 0);
expected_actions[2].emplace_back(7, ActionForTest::ActionType::kCreateThread,
20);
// Order isn't deterministic, since threads only start running once all the
// initial threads are created, and as a result the logging order isn't
// deterministic,
EXPECT_THAT(executed_actions, UnorderedElementsAreArray(expected_actions));
}
} // namespace sequence_manager
} // namespace base
#include "third_party/blink/renderer/platform/scheduler/base/simple_thread_impl.h"
namespace base {
namespace sequence_manager {
SimpleThreadImpl::SimpleThreadImpl(ThreadCallback callback,
TimeTicks initial_time)
: SimpleThread("TestThread"),
callback_(std::move(callback)),
initial_time_(initial_time) {}
void SimpleThreadImpl::Run() {
std::unique_ptr<ThreadData> thread_data =
std::make_unique<ThreadData>(initial_time_);
thread_data_ = thread_data.get();
std::move(callback_).Run(thread_data_);
thread_can_shutdown_.Wait();
}
SimpleThreadImpl::~SimpleThreadImpl() {
thread_can_shutdown_.Signal();
Join();
}
ThreadData* SimpleThreadImpl::thread_data() const {
return thread_data_;
}
} // namespace sequence_manager
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SIMPLE_THREAD_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_SIMPLE_THREAD_IMPL_H_
#include "base/callback.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/simple_thread.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/base/thread_data.h"
namespace base {
namespace sequence_manager {
// Used by the ThreadPoolManager to create threads that do not have an
// associated message loop, since we want to use base::TestMockTimeTaskRunner to
// control the task execution and the clock of the thread.
class PLATFORM_EXPORT SimpleThreadImpl : public SimpleThread {
public:
using ThreadCallback = base::OnceCallback<void(ThreadData*)>;
SimpleThreadImpl(ThreadCallback callback, TimeTicks initial_time);
~SimpleThreadImpl() override;
ThreadData* thread_data() const;
private:
// This doesn't terminate until |this| object is destructed.
void Run() override;
ThreadCallback callback_;
// Time in which the thread is created.
TimeTicks initial_time_;
// The object pointed to by |thread_data_| is created and destructed from the
// Run function. This is necessary since it has to be constructed from the
// thread it should be bound to and destructed from the same thread.
ThreadData* thread_data_;
// Used by the Run function to only terminate when |this| is destructed, and
// this is used so that |thread_data_| will live as long as |this|.
WaitableEvent thread_can_shutdown_;
};
} // namespace sequence_manager
} // namespace base
#endif
#include "third_party/blink/renderer/platform/scheduler/base/thread_data.h"
#include "base/task/sequence_manager/test/test_task_queue.h"
#include "base/threading/thread_task_runner_handle.h"
namespace base {
namespace sequence_manager {
ThreadData::ThreadData(TimeTicks initial_time) {
test_task_runner_ = WrapRefCounted(
new TestMockTimeTaskRunner(TestMockTimeTaskRunner::Type::kBoundToThread));
DCHECK(!(initial_time - TimeTicks()).is_zero())
<< "A zero clock is not allowed as empty TimeTicks have a special value "
"(i.e. base::TimeTicks::is_null())";
test_task_runner_->AdvanceMockTickClock(initial_time - TimeTicks());
manager_ =
SequenceManagerForTest::Create(nullptr, ThreadTaskRunnerHandle::Get(),
test_task_runner_->GetMockTickClock());
TaskQueue::Spec spec = TaskQueue::Spec("default_task_queue");
task_queues_.emplace_back(manager_->CreateTaskQueue<TestTaskQueue>(spec));
}
ThreadData::~ThreadData() = default;
} // namespace sequence_manager
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_DATA_H_
#include <memory>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/task/sequence_manager/test/test_task_queue.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_fuzzer_processor.h"
namespace base {
namespace sequence_manager {
class PLATFORM_EXPORT ThreadData {
public:
struct TaskQueueWithVoters {
explicit TaskQueueWithVoters(scoped_refptr<TestTaskQueue> task_queue)
: queue(std::move(task_queue)){};
scoped_refptr<TestTaskQueue> queue;
std::vector<std::unique_ptr<TaskQueue::QueueEnabledVoter>> voters;
};
// |initial_time| is the time in which |this| was instantiated.
explicit ThreadData(TimeTicks initial_time);
~ThreadData();
// Bound to the thread in which this object was instantiated. Used to
// control the clock of the sequence manager.
scoped_refptr<TestMockTimeTaskRunner> test_task_runner_;
std::unique_ptr<SequenceManagerForTest> manager_;
// For testing purposes, this should follow the order in which queues
// were created on the thread in which |this| was instantiated.
std::vector<TaskQueueWithVoters> task_queues_;
// Used to be able to cancel pending tasks from the sequence manager. For
// testing purposes, this should follow the order in which the tasks were
// posted to the thread in which |this| was instantiated.
std::vector<std::unique_ptr<SequenceManagerFuzzerProcessor::Task>>
pending_tasks_;
// For Testing. Used to log tasks in their order of execution on the
// thread in which |this| was instantiated.
std::vector<SequenceManagerFuzzerProcessor::TaskForTest> ordered_tasks_;
// For Testing. Used to log actions in their order of execution on the
// thread in which |this| was instantiated.
std::vector<SequenceManagerFuzzerProcessor::ActionForTest> ordered_actions_;
THREAD_CHECKER(thread_checker_);
};
} // namespace sequence_manager
} // namespace base
#endif
#include "third_party/blink/renderer/platform/scheduler/base/thread_pool_manager.h"
#include <algorithm>
#include "base/bind.h"
#include "third_party/blink/renderer/platform/scheduler/base/sequence_manager_fuzzer_processor.h"
#include "third_party/blink/renderer/platform/scheduler/base/simple_thread_impl.h"
#include "third_party/blink/renderer/platform/scheduler/base/thread_data.h"
namespace base {
namespace sequence_manager {
ThreadPoolManager::ThreadPoolManager(SequenceManagerFuzzerProcessor* processor)
: processor_(processor),
next_time_(TimeTicks::Max()),
ready_to_compute_time_(&lock_),
ready_to_advance_time_(&lock_),
ready_to_terminate_(&lock_),
ready_to_execute_threads_(&lock_),
ready_for_next_round_(&lock_),
threads_waiting_to_compute_time_(0),
threads_waiting_to_advance_time_(0),
threads_ready_for_next_round_(0),
threads_ready_to_terminate_(0),
all_threads_ready_(true),
initial_threads_created_(false) {
DCHECK(processor_);
};
ThreadPoolManager::~ThreadPoolManager() = default;
void ThreadPoolManager::CreateThread(
const google::protobuf::RepeatedPtrField<
SequenceManagerTestDescription::Action>& initial_thread_actions,
TimeTicks time) {
SimpleThreadImpl* thread;
{
AutoLock lock(lock_);
threads_.push_back(std::make_unique<SimpleThreadImpl>(
BindOnce(&ThreadPoolManager::StartThread, Unretained(this),
initial_thread_actions),
time));
thread = threads_.back().get();
}
thread->Start();
}
void ThreadPoolManager::StartThread(
const google::protobuf::RepeatedPtrField<
SequenceManagerTestDescription::Action>& initial_thread_actions,
ThreadData* thread_data) {
{
AutoLock lock(lock_);
while (!initial_threads_created_)
ready_to_execute_threads_.Wait();
}
processor_->ExecuteThread(thread_data, initial_thread_actions);
}
void ThreadPoolManager::AdvanceClockSynchronouslyByPendingTaskDelay(
ThreadData* thread_data) {
DCHECK_CALLED_ON_VALID_THREAD(thread_data->thread_checker_);
ThreadReadyToComputeTime();
{
AutoLock lock(lock_);
while (threads_waiting_to_compute_time_ != threads_.size())
ready_to_compute_time_.Wait();
next_time_ = std::min(
next_time_,
thread_data->test_task_runner_->GetMockTickClock()->NowTicks() +
std::max(TimeDelta::FromMilliseconds(0),
thread_data->test_task_runner_->NextPendingTaskDelay()));
threads_waiting_to_advance_time_++;
if (threads_waiting_to_advance_time_ == threads_.size()) {
threads_waiting_to_compute_time_ = 0;
ready_to_advance_time_.Broadcast();
}
}
AdvanceThreadClock(thread_data);
}
void ThreadPoolManager::AdvanceClockSynchronouslyToTime(ThreadData* thread_data,
TimeTicks time) {
ThreadReadyToComputeTime();
{
AutoLock lock(lock_);
while (threads_waiting_to_compute_time_ != threads_.size())
ready_to_compute_time_.Wait();
next_time_ = std::min(next_time_, time);
threads_waiting_to_advance_time_++;
if (threads_waiting_to_advance_time_ == threads_.size()) {
threads_waiting_to_compute_time_ = 0;
ready_to_advance_time_.Broadcast();
}
}
AdvanceThreadClock(thread_data);
}
void ThreadPoolManager::ThreadReadyToComputeTime() {
AutoLock lock(lock_);
while (!all_threads_ready_)
ready_for_next_round_.Wait();
threads_waiting_to_compute_time_++;
if (threads_waiting_to_compute_time_ == threads_.size()) {
all_threads_ready_ = false;
ready_to_compute_time_.Broadcast();
}
}
void ThreadPoolManager::AdvanceThreadClock(ThreadData* thread_data) {
AutoLock lock(lock_);
while (threads_waiting_to_advance_time_ != threads_.size())
ready_to_advance_time_.Wait();
thread_data->test_task_runner_->AdvanceMockTickClock(
next_time_ -
thread_data->test_task_runner_->GetMockTickClock()->NowTicks());
threads_ready_for_next_round_++;
if (threads_ready_for_next_round_ == threads_.size()) {
threads_waiting_to_advance_time_ = 0;
threads_ready_for_next_round_ = 0;
all_threads_ready_ = true;
next_time_ = TimeTicks::Max();
ready_for_next_round_.Broadcast();
}
}
void ThreadPoolManager::StartInitialThreads() {
{
AutoLock lock(lock_);
initial_threads_created_ = true;
}
ready_to_execute_threads_.Broadcast();
}
void ThreadPoolManager::WaitForAllThreads() {
if (threads_.empty())
return;
AutoLock lock(lock_);
while (threads_ready_to_terminate_ != threads_.size())
ready_to_terminate_.Wait();
}
void ThreadPoolManager::ThreadDone() {
AutoLock lock(lock_);
threads_ready_to_terminate_++;
if (threads_ready_to_terminate_ == threads_.size()) {
// Only the main thread waits for this event.
ready_to_terminate_.Signal();
}
}
const std::vector<std::unique_ptr<SimpleThreadImpl>>&
ThreadPoolManager::threads() {
return threads_;
}
} // namespace sequence_manager
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_POOL_MANAGER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_BASE_THREAD_POOL_MANAGER_H_
#include <memory>
#include <vector>
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/base/proto/sequence_manager_test_description.pb.h"
namespace base {
namespace sequence_manager {
class SequenceManagerFuzzerProcessor;
class SimpleThreadImpl;
class ThreadData;
// Used by the SequenceManagerFuzzerProcessor to manage threads and synchronize
// their clocks.
class PLATFORM_EXPORT ThreadPoolManager {
public:
explicit ThreadPoolManager(SequenceManagerFuzzerProcessor* processor);
~ThreadPoolManager();
// |time| is the virtual time in which the thread is created. The virtual time
// is controlled by |this| and synchronized for all the threads it owns.
void CreateThread(
const google::protobuf::RepeatedPtrField<
SequenceManagerTestDescription::Action>& initial_thread_actions,
TimeTicks time);
// Advances the mock tick clock of all the threads synchronously.
// Note that this doesn't guarantee advancing the thread's clock to |time|.
// The clock is advanced to the minimum desired time of all the owned threads.
void AdvanceClockSynchronouslyToTime(ThreadData* thread_data, TimeTicks time);
// Advances the mock tick clock of all the threads synchronously.
// Note that this doesn't guarantee advancing the thread's clock by the next
// pending task delay. The clock is advanced to the minimum desired time of
// all the owned threads.
void AdvanceClockSynchronouslyByPendingTaskDelay(ThreadData* thread_data);
// Used by a thread to notify the thread manager that it is done executing the
// thread actions passed to ThreadPoolManager::CreateThread.
void ThreadDone();
void StartInitialThreads();
// Used by the processor to wait for all of the threads to finish executing
// the actions passed by ThreadPoolManager::CreateThread. Note that
// the threads are not terminated until |this| gets destructed.
void WaitForAllThreads();
const std::vector<std::unique_ptr<SimpleThreadImpl>>& threads();
private:
void StartThread(
const google::protobuf::RepeatedPtrField<
SequenceManagerTestDescription::Action>& initial_thread_actions,
ThreadData* thread_data);
// Helper function used by AdvanceClockSynchronouslyToTime and
// AdvanceClockSynchronouslyByPendingTaskDelay to notify the manager when all
// threads are ready to compute their next desired time.
void ThreadReadyToComputeTime();
// Helper function used by AdvanceClockSynchronouslyToTime and
// AdvanceClockSynchronouslyByPendingTaskDelay to advance the thread's clock
// to |next_time_|. Note that this function waits until all owned threads have
// voted on the value of |next_time_|.
void AdvanceThreadClock(ThreadData* thread_data);
// Owner of this class.
SequenceManagerFuzzerProcessor* const processor_;
// Used to protect all the members below.
Lock lock_;
// Used to synchronize virtual time across all threads.
TimeTicks next_time_;
// Condition to ensure that all threads have their desired next time
// computed, and thus the global |next_time_| can be computed as their
// minimum value.
ConditionVariable ready_to_compute_time_;
// Condition that |next_time_| is computed and that all threads can advance
// their clock to |next_time_|.
ConditionVariable ready_to_advance_time_;
// Condition that all threads are done and the program is ready to
// terminate.
ConditionVariable ready_to_terminate_;
// Condition that threads can start running. This is needed to make sure all
// of the initial (program entry points) threads were created before any
// thread starts running.
ConditionVariable ready_to_execute_threads_;
// A round starts by all of the threads waiting to compute their desired
// next time and ends by all of them advancing their clocks.
ConditionVariable ready_for_next_round_;
uint64_t threads_waiting_to_compute_time_;
uint64_t threads_waiting_to_advance_time_;
// Number of threads done advancing their clocks and are ready for the next
// round.
uint64_t threads_ready_for_next_round_;
// Number of threads done executing their sequence of actions.
uint64_t threads_ready_to_terminate_;
// Used to notify the condition |ready_for_next_round_|.
bool all_threads_ready_;
// Used to notify the condition |ready_to_start_threads_|.
bool initial_threads_created_;
// Threads that are being managed/synchronized. For unit testing purposes,
// make sure not to create threads at the same time (if the ordering matters)
// since in this case the order will not be deterministic.
std::vector<std::unique_ptr<SimpleThreadImpl>> threads_;
};
} // namespace sequence_manager
} // namespace base
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment