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;
}
......
......@@ -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();
scoped_refptr<TestTaskQueue> queue;
std::vector<std::unique_ptr<TaskQueue::QueueEnabledVoter>> voters;
};
explicit SequenceManagerFuzzerProcessor(bool log_for_testing);
~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
......
#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