diff options
author | reveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-14 01:06:55 +0000 |
---|---|---|
committer | reveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-14 01:06:55 +0000 |
commit | 24891e6990f1f302594a2ce1e3c6609dbbb9b7dd (patch) | |
tree | ce76dace03060f745817e90ad01a0c9d3b4bee3d /cc/base | |
parent | d1ef0ec9bf488963a1c5b0700af051a872ebc454 (diff) | |
download | chromium_src-24891e6990f1f302594a2ce1e3c6609dbbb9b7dd.zip chromium_src-24891e6990f1f302594a2ce1e3c6609dbbb9b7dd.tar.gz chromium_src-24891e6990f1f302594a2ce1e3c6609dbbb9b7dd.tar.bz2 |
cc: Move WorkerPool from cc/base to cc/resources.
R=jamesr
BUG=
Review URL: https://chromiumcodereview.appspot.com/17004002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@206263 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/base')
-rw-r--r-- | cc/base/worker_pool.cc | 461 | ||||
-rw-r--r-- | cc/base/worker_pool.h | 154 | ||||
-rw-r--r-- | cc/base/worker_pool_perftest.cc | 244 | ||||
-rw-r--r-- | cc/base/worker_pool_unittest.cc | 206 |
4 files changed, 0 insertions, 1065 deletions
diff --git a/cc/base/worker_pool.cc b/cc/base/worker_pool.cc deleted file mode 100644 index bb910e9..0000000 --- a/cc/base/worker_pool.cc +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2013 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. - -#include "cc/base/worker_pool.h" - -#if defined(OS_ANDROID) -// TODO(epenner): Move thread priorities to base. (crbug.com/170549) -#include <sys/resource.h> -#endif - -#include <map> - -#include "base/bind.h" -#include "base/containers/hash_tables.h" -#include "base/debug/trace_event.h" -#include "base/strings/stringprintf.h" -#include "base/threading/simple_thread.h" -#include "base/threading/thread_restrictions.h" -#include "cc/base/scoped_ptr_deque.h" - -namespace cc { - -namespace internal { - -WorkerPoolTask::WorkerPoolTask() - : did_schedule_(false), - did_run_(false), - did_complete_(false) { -} - -WorkerPoolTask::WorkerPoolTask(TaskVector* dependencies) - : did_schedule_(false), - did_run_(false), - did_complete_(false) { - dependencies_.swap(*dependencies); -} - -WorkerPoolTask::~WorkerPoolTask() { - DCHECK_EQ(did_schedule_, did_complete_); - DCHECK(!did_run_ || did_schedule_); - DCHECK(!did_run_ || did_complete_); -} - -void WorkerPoolTask::DidSchedule() { - DCHECK(!did_complete_); - did_schedule_ = true; -} - -void WorkerPoolTask::WillRun() { - DCHECK(did_schedule_); - DCHECK(!did_complete_); - DCHECK(!did_run_); -} - -void WorkerPoolTask::DidRun() { - did_run_ = true; -} - -void WorkerPoolTask::DidComplete() { - DCHECK(did_schedule_); - DCHECK(!did_complete_); - did_complete_ = true; -} - -bool WorkerPoolTask::IsReadyToRun() const { - // TODO(reveman): Use counter to improve performance. - for (TaskVector::const_reverse_iterator it = dependencies_.rbegin(); - it != dependencies_.rend(); ++it) { - WorkerPoolTask* dependency = it->get(); - if (!dependency->HasFinishedRunning()) - return false; - } - return true; -} - -bool WorkerPoolTask::HasFinishedRunning() const { - return did_run_; -} - -bool WorkerPoolTask::HasCompleted() const { - return did_complete_; -} - -} // namespace internal - -// Internal to the worker pool. Any data or logic that needs to be -// shared between threads lives in this class. All members are guarded -// by |lock_|. -class WorkerPool::Inner : public base::DelegateSimpleThread::Delegate { - public: - Inner(size_t num_threads, const std::string& thread_name_prefix); - virtual ~Inner(); - - void Shutdown(); - - // Schedule running of tasks in |graph|. Tasks previously scheduled but - // no longer needed will be canceled unless already running. Canceled - // tasks are moved to |completed_tasks_| without being run. The result - // is that once scheduled, a task is guaranteed to end up in the - // |completed_tasks_| queue even if they later get canceled by another - // call to SetTaskGraph(). - void SetTaskGraph(TaskGraph* graph); - - // Collect all completed tasks in |completed_tasks|. - void CollectCompletedTasks(TaskDeque* completed_tasks); - - private: - // Overridden from base::DelegateSimpleThread: - virtual void Run() OVERRIDE; - - // This lock protects all members of this class except - // |worker_pool_on_origin_thread_|. Do not read or modify anything - // without holding this lock. Do not block while holding this lock. - mutable base::Lock lock_; - - // Condition variable that is waited on by worker threads until new - // tasks are ready to run or shutdown starts. - base::ConditionVariable has_ready_to_run_tasks_cv_; - - // Provides each running thread loop with a unique index. First thread - // loop index is 0. - unsigned next_thread_index_; - - // Set during shutdown. Tells workers to exit when no more tasks - // are pending. - bool shutdown_; - - // This set contains all pending tasks. - GraphNodeMap pending_tasks_; - - // Ordered set of tasks that are ready to run. - // TODO(reveman): priority_queue might be more efficient. - typedef std::map<unsigned, internal::WorkerPoolTask*> TaskMap; - TaskMap ready_to_run_tasks_; - - // This set contains all currently running tasks. - GraphNodeMap running_tasks_; - - // Completed tasks not yet collected by origin thread. - TaskDeque completed_tasks_; - - ScopedPtrDeque<base::DelegateSimpleThread> workers_; - - DISALLOW_COPY_AND_ASSIGN(Inner); -}; - -WorkerPool::Inner::Inner( - size_t num_threads, const std::string& thread_name_prefix) - : lock_(), - has_ready_to_run_tasks_cv_(&lock_), - next_thread_index_(0), - shutdown_(false) { - base::AutoLock lock(lock_); - - while (workers_.size() < num_threads) { - scoped_ptr<base::DelegateSimpleThread> worker = make_scoped_ptr( - new base::DelegateSimpleThread( - this, - thread_name_prefix + - base::StringPrintf( - "Worker%u", - static_cast<unsigned>(workers_.size() + 1)).c_str())); - worker->Start(); - workers_.push_back(worker.Pass()); - } -} - -WorkerPool::Inner::~Inner() { - base::AutoLock lock(lock_); - - DCHECK(shutdown_); - - DCHECK_EQ(0u, pending_tasks_.size()); - DCHECK_EQ(0u, ready_to_run_tasks_.size()); - DCHECK_EQ(0u, running_tasks_.size()); - DCHECK_EQ(0u, completed_tasks_.size()); -} - -void WorkerPool::Inner::Shutdown() { - { - base::AutoLock lock(lock_); - - DCHECK(!shutdown_); - shutdown_ = true; - - // Wake up a worker so it knows it should exit. This will cause all workers - // to exit as each will wake up another worker before exiting. - has_ready_to_run_tasks_cv_.Signal(); - } - - while (workers_.size()) { - scoped_ptr<base::DelegateSimpleThread> worker = workers_.take_front(); - // http://crbug.com/240453 - Join() is considered IO and will block this - // thread. See also http://crbug.com/239423 for further ideas. - base::ThreadRestrictions::ScopedAllowIO allow_io; - worker->Join(); - } -} - -void WorkerPool::Inner::SetTaskGraph(TaskGraph* graph) { - // It is OK to call SetTaskGraph() after shutdown if |graph| is empty. - DCHECK(graph->empty() || !shutdown_); - - GraphNodeMap new_pending_tasks; - GraphNodeMap new_running_tasks; - TaskMap new_ready_to_run_tasks; - - new_pending_tasks.swap(*graph); - - { - base::AutoLock lock(lock_); - - // First remove all completed tasks from |new_pending_tasks|. - for (TaskDeque::iterator it = completed_tasks_.begin(); - it != completed_tasks_.end(); ++it) { - internal::WorkerPoolTask* task = it->get(); - new_pending_tasks.take_and_erase(task); - } - - // Move tasks not present in |new_pending_tasks| to |completed_tasks_|. - for (GraphNodeMap::iterator it = pending_tasks_.begin(); - it != pending_tasks_.end(); ++it) { - internal::WorkerPoolTask* task = it->first; - - // Task has completed if not present in |new_pending_tasks|. - if (!new_pending_tasks.contains(task)) - completed_tasks_.push_back(task); - } - - // Build new running task set. - for (GraphNodeMap::iterator it = running_tasks_.begin(); - it != running_tasks_.end(); ++it) { - internal::WorkerPoolTask* task = it->first; - // Transfer scheduled task value from |new_pending_tasks| to - // |new_running_tasks| if currently running. Value must be set to - // NULL if |new_pending_tasks| doesn't contain task. This does - // the right in both cases. - new_running_tasks.set(task, new_pending_tasks.take_and_erase(task)); - } - - // Build new "ready to run" tasks queue. - for (GraphNodeMap::iterator it = new_pending_tasks.begin(); - it != new_pending_tasks.end(); ++it) { - internal::WorkerPoolTask* task = it->first; - - // Completed tasks should not exist in |new_pending_tasks|. - DCHECK(!task->HasFinishedRunning()); - - // Call DidSchedule() to indicate that this task has been scheduled. - // Note: This is only for debugging purposes. - task->DidSchedule(); - - DCHECK_EQ(0u, new_ready_to_run_tasks.count(it->second->priority())); - if (task->IsReadyToRun()) - new_ready_to_run_tasks[it->second->priority()] = task; - } - - // Swap task sets. - // Note: old tasks are intentionally destroyed after releasing |lock_|. - pending_tasks_.swap(new_pending_tasks); - running_tasks_.swap(new_running_tasks); - ready_to_run_tasks_.swap(new_ready_to_run_tasks); - - // If there is more work available, wake up worker thread. - if (!ready_to_run_tasks_.empty()) - has_ready_to_run_tasks_cv_.Signal(); - } -} - -void WorkerPool::Inner::CollectCompletedTasks(TaskDeque* completed_tasks) { - base::AutoLock lock(lock_); - - DCHECK_EQ(0u, completed_tasks->size()); - completed_tasks->swap(completed_tasks_); -} - -void WorkerPool::Inner::Run() { -#if defined(OS_ANDROID) - base::PlatformThread::SetThreadPriority( - base::PlatformThread::CurrentHandle(), - base::kThreadPriority_Background); -#endif - - base::AutoLock lock(lock_); - - // Get a unique thread index. - int thread_index = next_thread_index_++; - - while (true) { - if (ready_to_run_tasks_.empty()) { - // Exit when shutdown is set and no more tasks are pending. - if (shutdown_ && pending_tasks_.empty()) - break; - - // Wait for more tasks. - has_ready_to_run_tasks_cv_.Wait(); - continue; - } - - // Take top priority task from |ready_to_run_tasks_|. - scoped_refptr<internal::WorkerPoolTask> task( - ready_to_run_tasks_.begin()->second); - ready_to_run_tasks_.erase(ready_to_run_tasks_.begin()); - - // Move task from |pending_tasks_| to |running_tasks_|. - DCHECK(pending_tasks_.contains(task.get())); - DCHECK(!running_tasks_.contains(task.get())); - running_tasks_.set(task.get(), pending_tasks_.take_and_erase(task.get())); - - // There may be more work available, so wake up another worker thread. - has_ready_to_run_tasks_cv_.Signal(); - - // Call WillRun() before releasing |lock_| and running task. - task->WillRun(); - - { - base::AutoUnlock unlock(lock_); - - task->RunOnThread(thread_index); - } - - // This will mark task as finished running. - task->DidRun(); - - // Now iterate over all dependents to check if they are ready to run. - scoped_ptr<GraphNode> node = running_tasks_.take_and_erase(task.get()); - if (node) { - typedef internal::WorkerPoolTask::TaskVector TaskVector; - for (TaskVector::const_iterator it = node->dependents().begin(); - it != node->dependents().end(); ++it) { - GraphNodeMap::iterator dependent_it = pending_tasks_.find(it->get()); - DCHECK(dependent_it != pending_tasks_.end()); - - internal::WorkerPoolTask* dependent = dependent_it->first; - if (!dependent->IsReadyToRun()) - continue; - - // Task is ready. Add it to |ready_to_run_tasks_|. - GraphNode* dependent_node = dependent_it->second; - unsigned priority = dependent_node->priority(); - DCHECK(!ready_to_run_tasks_.count(priority) || - ready_to_run_tasks_[priority] == dependent); - ready_to_run_tasks_[priority] = dependent; - } - } - - // Finally add task to |completed_tasks_|. - completed_tasks_.push_back(task); - } - - // We noticed we should exit. Wake up the next worker so it knows it should - // exit as well (because the Shutdown() code only signals once). - has_ready_to_run_tasks_cv_.Signal(); -} - -WorkerPool::GraphNode::GraphNode( - internal::WorkerPoolTask* dependent, unsigned priority) - : priority_(priority) { - if (dependent) - dependents_.push_back(dependent); -} - -WorkerPool::GraphNode::~GraphNode() { -} - -void WorkerPool::GraphNode::AddDependent(internal::WorkerPoolTask* dependent) { - DCHECK(dependent); - dependents_.push_back(dependent); -} - -WorkerPool::WorkerPool(size_t num_threads, - const std::string& thread_name_prefix) - : in_dispatch_completion_callbacks_(false), - inner_(make_scoped_ptr(new Inner(num_threads, thread_name_prefix))) { -} - -WorkerPool::~WorkerPool() { -} - -void WorkerPool::Shutdown() { - TRACE_EVENT0("cc", "WorkerPool::Shutdown"); - - DCHECK(!in_dispatch_completion_callbacks_); - - inner_->Shutdown(); -} - -void WorkerPool::CheckForCompletedTasks() { - TRACE_EVENT0("cc", "WorkerPool::CheckForCompletedTasks"); - - DCHECK(!in_dispatch_completion_callbacks_); - - TaskDeque completed_tasks; - inner_->CollectCompletedTasks(&completed_tasks); - DispatchCompletionCallbacks(&completed_tasks); -} - -void WorkerPool::DispatchCompletionCallbacks(TaskDeque* completed_tasks) { - TRACE_EVENT0("cc", "WorkerPool::DispatchCompletionCallbacks"); - - // Worker pool instance is not reentrant while processing completed tasks. - in_dispatch_completion_callbacks_ = true; - - while (!completed_tasks->empty()) { - internal::WorkerPoolTask* task = completed_tasks->front().get(); - - task->DidComplete(); - task->DispatchCompletionCallback(); - - completed_tasks->pop_front(); - } - - in_dispatch_completion_callbacks_ = false; -} - -void WorkerPool::SetTaskGraph(TaskGraph* graph) { - TRACE_EVENT0("cc", "WorkerPool::SetTaskGraph"); - - DCHECK(!in_dispatch_completion_callbacks_); - - inner_->SetTaskGraph(graph); -} - -// static -unsigned WorkerPool::BuildTaskGraphRecursive( - internal::WorkerPoolTask* task, - internal::WorkerPoolTask* dependent, - unsigned priority, - TaskGraph* tasks) { - // Skip sub-tree if task has already completed. - if (task->HasCompleted()) - return priority; - - GraphNodeMap::iterator it = tasks->find(task); - if (it != tasks->end()) { - it->second->AddDependent(dependent); - return priority; - } - - typedef internal::WorkerPoolTask::TaskVector TaskVector; - for (TaskVector::iterator dependency_it = task->dependencies().begin(); - dependency_it != task->dependencies().end(); ++dependency_it) { - internal::WorkerPoolTask* dependency = dependency_it->get(); - priority = BuildTaskGraphRecursive(dependency, task, priority, tasks); - } - - tasks->set(task, make_scoped_ptr(new GraphNode(dependent, priority))); - - return priority + 1; -} - -// static -void WorkerPool::BuildTaskGraph( - internal::WorkerPoolTask* root, TaskGraph* tasks) { - const unsigned kBasePriority = 0u; - if (root) - BuildTaskGraphRecursive(root, NULL, kBasePriority, tasks); -} - -} // namespace cc diff --git a/cc/base/worker_pool.h b/cc/base/worker_pool.h deleted file mode 100644 index eeffb56..0000000 --- a/cc/base/worker_pool.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2013 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 CC_BASE_WORKER_POOL_H_ -#define CC_BASE_WORKER_POOL_H_ - -#include <deque> -#include <string> -#include <vector> - -#include "base/cancelable_callback.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop.h" -#include "cc/base/cc_export.h" -#include "cc/base/scoped_ptr_hash_map.h" - -namespace cc { -namespace internal { - -class CC_EXPORT WorkerPoolTask - : public base::RefCountedThreadSafe<WorkerPoolTask> { - public: - typedef std::vector<scoped_refptr<WorkerPoolTask> > TaskVector; - - virtual void RunOnThread(unsigned thread_index) = 0; - virtual void DispatchCompletionCallback() = 0; - - void DidSchedule(); - void WillRun(); - void DidRun(); - void DidComplete(); - - bool IsReadyToRun() const; - bool HasFinishedRunning() const; - bool HasCompleted() const; - - TaskVector& dependencies() { return dependencies_; } - - protected: - friend class base::RefCountedThreadSafe<WorkerPoolTask>; - - WorkerPoolTask(); - explicit WorkerPoolTask(TaskVector* dependencies); - virtual ~WorkerPoolTask(); - - private: - bool did_schedule_; - bool did_run_; - bool did_complete_; - TaskVector dependencies_; -}; - -} // namespace internal -} // namespace cc - -#if defined(COMPILER_GCC) -namespace BASE_HASH_NAMESPACE { -template <> struct hash<cc::internal::WorkerPoolTask*> { - size_t operator()(cc::internal::WorkerPoolTask* ptr) const { - return hash<size_t>()(reinterpret_cast<size_t>(ptr)); - } -}; -} // namespace BASE_HASH_NAMESPACE -#endif // COMPILER - -namespace cc { - -// A worker thread pool that runs tasks provided by task graph and -// guarantees completion of all pending tasks at shutdown. -class CC_EXPORT WorkerPool { - public: - virtual ~WorkerPool(); - - // Tells the worker pool to shutdown and returns once all pending tasks have - // completed. - virtual void Shutdown(); - - // Force a check for completed tasks. - virtual void CheckForCompletedTasks(); - - protected: - class CC_EXPORT GraphNode { - public: - GraphNode(internal::WorkerPoolTask* dependent, unsigned priority); - ~GraphNode(); - - void AddDependent(internal::WorkerPoolTask* dependent); - - const internal::WorkerPoolTask::TaskVector& dependents() const { - return dependents_; - } - unsigned priority() const { return priority_; } - - private: - internal::WorkerPoolTask::TaskVector dependents_; - unsigned priority_; - - DISALLOW_COPY_AND_ASSIGN(GraphNode); - }; - typedef ScopedPtrHashMap<internal::WorkerPoolTask*, GraphNode> GraphNodeMap; - typedef GraphNodeMap TaskGraph; - - WorkerPool(size_t num_threads, const std::string& thread_name_prefix); - - // Schedule running of tasks in |graph|. Any previously scheduled tasks - // that are not already running will be canceled. Canceled tasks don't run - // but completion of them is still processed. - void SetTaskGraph(TaskGraph* graph); - - // BuildTaskGraph() takes a task tree as input and constructs a - // unique set of tasks with edges between dependencies pointing in - // the direction of the dependents. Each task is given a unique priority - // which is currently the same as the DFS traversal order. - // - // Input: Output: - // - // root task4 Task | Priority (lower is better) - // / \ / \ -------+--------------------------- - // task1 task2 task3 task2 root | 4 - // | | | | task1 | 2 - // task3 | task1 | task2 | 3 - // | | \ / task3 | 1 - // task4 task4 root task4 | 0 - // - // The output can be used to efficiently maintain a queue of - // "ready to run" tasks. - static unsigned BuildTaskGraphRecursive( - internal::WorkerPoolTask* task, - internal::WorkerPoolTask* dependent, - unsigned priority, - TaskGraph* tasks); - static void BuildTaskGraph( - internal::WorkerPoolTask* root, TaskGraph* tasks); - - private: - class Inner; - friend class Inner; - - typedef std::deque<scoped_refptr<internal::WorkerPoolTask> > TaskDeque; - - void DispatchCompletionCallbacks(TaskDeque* completed_tasks); - - bool in_dispatch_completion_callbacks_; - - // Hide the gory details of the worker pool in |inner_|. - const scoped_ptr<Inner> inner_; -}; - -} // namespace cc - -#endif // CC_BASE_WORKER_POOL_H_ diff --git a/cc/base/worker_pool_perftest.cc b/cc/base/worker_pool_perftest.cc deleted file mode 100644 index f83b886..0000000 --- a/cc/base/worker_pool_perftest.cc +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2013 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. - -#include "cc/base/worker_pool.h" - -#include "base/time.h" -#include "cc/base/completion_event.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { - -namespace { - -static const int kTimeLimitMillis = 2000; -static const int kWarmupRuns = 5; -static const int kTimeCheckInterval = 10; - -class PerfTaskImpl : public internal::WorkerPoolTask { - public: - explicit PerfTaskImpl(internal::WorkerPoolTask::TaskVector* dependencies) - : internal::WorkerPoolTask(dependencies) {} - - // Overridden from internal::WorkerPoolTask: - virtual void RunOnThread(unsigned thread_index) OVERRIDE {} - virtual void DispatchCompletionCallback() OVERRIDE {} - - private: - virtual ~PerfTaskImpl() {} -}; - -class PerfControlTaskImpl : public internal::WorkerPoolTask { - public: - explicit PerfControlTaskImpl( - internal::WorkerPoolTask::TaskVector* dependencies) - : internal::WorkerPoolTask(dependencies), - did_start_(new CompletionEvent), - can_finish_(new CompletionEvent) {} - - // Overridden from internal::WorkerPoolTask: - virtual void RunOnThread(unsigned thread_index) OVERRIDE { - did_start_->Signal(); - can_finish_->Wait(); - } - virtual void DispatchCompletionCallback() OVERRIDE {} - - void WaitForTaskToStartRunning() { - did_start_->Wait(); - } - - void AllowTaskToFinish() { - can_finish_->Signal(); - } - - private: - virtual ~PerfControlTaskImpl() {} - - scoped_ptr<CompletionEvent> did_start_; - scoped_ptr<CompletionEvent> can_finish_; -}; - -class PerfWorkerPool : public WorkerPool { - public: - PerfWorkerPool() : WorkerPool(1, "test") {} - virtual ~PerfWorkerPool() {} - - static scoped_ptr<PerfWorkerPool> Create() { - return make_scoped_ptr(new PerfWorkerPool); - } - - void BuildTaskGraph(internal::WorkerPoolTask* root) { - graph_.clear(); - WorkerPool::BuildTaskGraph(root, &graph_); - } - - void ScheduleTasks() { - SetTaskGraph(&graph_); - } - - private: - TaskGraph graph_; -}; - -class WorkerPoolPerfTest : public testing::Test { - public: - WorkerPoolPerfTest() : num_runs_(0) {} - - // Overridden from testing::Test: - virtual void SetUp() OVERRIDE { - worker_pool_ = PerfWorkerPool::Create(); - } - virtual void TearDown() OVERRIDE { - worker_pool_->Shutdown(); - worker_pool_->CheckForCompletedTasks(); - } - - void EndTest() { - elapsed_ = base::TimeTicks::HighResNow() - start_time_; - } - - void AfterTest(const std::string test_name) { - // Format matches chrome/test/perf/perf_test.h:PrintResult - printf("*RESULT %s: %.2f runs/s\n", - test_name.c_str(), - num_runs_ / elapsed_.InSecondsF()); - } - - void CreateTasks(internal::WorkerPoolTask::TaskVector* dependencies, - unsigned current_depth, - unsigned max_depth, - unsigned num_children_per_node) { - internal::WorkerPoolTask::TaskVector children; - if (current_depth < max_depth) { - for (unsigned i = 0; i < num_children_per_node; ++i) { - CreateTasks(&children, - current_depth + 1, - max_depth, - num_children_per_node); - } - } else if (leaf_task_.get()) { - children.push_back(leaf_task_); - } - dependencies->push_back(make_scoped_refptr(new PerfTaskImpl(&children))); - } - - bool DidRun() { - ++num_runs_; - if (num_runs_ == kWarmupRuns) - start_time_ = base::TimeTicks::HighResNow(); - - if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) { - base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_; - if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) { - elapsed_ = elapsed; - return false; - } - } - - return true; - } - - void RunBuildTaskGraphTest(const std::string test_name, - unsigned max_depth, - unsigned num_children_per_node) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; - internal::WorkerPoolTask::TaskVector children; - CreateTasks(&children, 0, max_depth, num_children_per_node); - scoped_refptr<PerfTaskImpl> root_task( - make_scoped_refptr(new PerfTaskImpl(&children))); - do { - worker_pool_->BuildTaskGraph(root_task.get()); - } while (DidRun()); - - AfterTest(test_name); - } - - void RunScheduleTasksTest(const std::string test_name, - unsigned max_depth, - unsigned num_children_per_node) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; - do { - internal::WorkerPoolTask::TaskVector empty; - leaf_task_ = make_scoped_refptr(new PerfControlTaskImpl(&empty)); - internal::WorkerPoolTask::TaskVector children; - CreateTasks(&children, 0, max_depth, num_children_per_node); - scoped_refptr<PerfTaskImpl> root_task( - make_scoped_refptr(new PerfTaskImpl(&children))); - - worker_pool_->BuildTaskGraph(root_task.get()); - worker_pool_->ScheduleTasks(); - leaf_task_->WaitForTaskToStartRunning(); - worker_pool_->BuildTaskGraph(NULL); - worker_pool_->ScheduleTasks(); - worker_pool_->CheckForCompletedTasks(); - leaf_task_->AllowTaskToFinish(); - } while (DidRun()); - - AfterTest(test_name); - } - - void RunExecuteTasksTest(const std::string test_name, - unsigned max_depth, - unsigned num_children_per_node) { - start_time_ = base::TimeTicks(); - num_runs_ = 0; - do { - internal::WorkerPoolTask::TaskVector children; - CreateTasks(&children, 0, max_depth, num_children_per_node); - scoped_refptr<PerfControlTaskImpl> root_task( - make_scoped_refptr(new PerfControlTaskImpl(&children))); - - worker_pool_->BuildTaskGraph(root_task.get()); - worker_pool_->ScheduleTasks(); - root_task->WaitForTaskToStartRunning(); - root_task->AllowTaskToFinish(); - worker_pool_->CheckForCompletedTasks(); - } while (DidRun()); - - AfterTest(test_name); - } - - protected: - scoped_ptr<PerfWorkerPool> worker_pool_; - scoped_refptr<PerfControlTaskImpl> leaf_task_; - base::TimeTicks start_time_; - base::TimeDelta elapsed_; - int num_runs_; -}; - -TEST_F(WorkerPoolPerfTest, BuildTaskGraph) { - RunBuildTaskGraphTest("build_task_graph_1_10", 1, 10); - RunBuildTaskGraphTest("build_task_graph_1_1000", 1, 1000); - RunBuildTaskGraphTest("build_task_graph_2_10", 2, 10); - RunBuildTaskGraphTest("build_task_graph_5_5", 5, 5); - RunBuildTaskGraphTest("build_task_graph_10_2", 10, 2); - RunBuildTaskGraphTest("build_task_graph_1000_1", 1000, 1); - RunBuildTaskGraphTest("build_task_graph_10_1", 10, 1); -} - -TEST_F(WorkerPoolPerfTest, ScheduleTasks) { - RunScheduleTasksTest("schedule_tasks_1_10", 1, 10); - RunScheduleTasksTest("schedule_tasks_1_1000", 1, 1000); - RunScheduleTasksTest("schedule_tasks_2_10", 2, 10); - RunScheduleTasksTest("schedule_tasks_5_5", 5, 5); - RunScheduleTasksTest("schedule_tasks_10_2", 10, 2); - RunScheduleTasksTest("schedule_tasks_1000_1", 1000, 1); - RunScheduleTasksTest("schedule_tasks_10_1", 10, 1); -} - -TEST_F(WorkerPoolPerfTest, ExecuteTasks) { - RunExecuteTasksTest("execute_tasks_1_10", 1, 10); - RunExecuteTasksTest("execute_tasks_1_1000", 1, 1000); - RunExecuteTasksTest("execute_tasks_2_10", 2, 10); - RunExecuteTasksTest("execute_tasks_5_5", 5, 5); - RunExecuteTasksTest("execute_tasks_10_2", 10, 2); - RunExecuteTasksTest("execute_tasks_1000_1", 1000, 1); - RunExecuteTasksTest("execute_tasks_10_1", 10, 1); -} - -} // namespace - -} // namespace cc diff --git a/cc/base/worker_pool_unittest.cc b/cc/base/worker_pool_unittest.cc deleted file mode 100644 index 1a48ec4..0000000 --- a/cc/base/worker_pool_unittest.cc +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2013 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. - -#include "cc/base/worker_pool.h" - -#include <vector> - -#include "cc/base/completion_event.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { - -namespace { - -class FakeTaskImpl : public internal::WorkerPoolTask { - public: - FakeTaskImpl(const base::Closure& callback, - const base::Closure& reply, - internal::WorkerPoolTask::TaskVector* dependencies) - : internal::WorkerPoolTask(dependencies), - callback_(callback), - reply_(reply) { - } - FakeTaskImpl(const base::Closure& callback, const base::Closure& reply) - : callback_(callback), - reply_(reply) { - } - - // Overridden from internal::WorkerPoolTask: - virtual void RunOnThread(unsigned thread_index) OVERRIDE { - if (!callback_.is_null()) - callback_.Run(); - } - virtual void DispatchCompletionCallback() OVERRIDE { - if (!reply_.is_null()) - reply_.Run(); - } - - private: - virtual ~FakeTaskImpl() {} - - const base::Closure callback_; - const base::Closure reply_; -}; - -class FakeWorkerPool : public WorkerPool { - public: - FakeWorkerPool() : WorkerPool(1, "test") {} - virtual ~FakeWorkerPool() {} - - static scoped_ptr<FakeWorkerPool> Create() { - return make_scoped_ptr(new FakeWorkerPool); - } - - void ScheduleTasks(const base::Closure& callback, - const base::Closure& reply, - const base::Closure& dependency, - int count) { - scoped_refptr<FakeTaskImpl> dependency_task( - new FakeTaskImpl(dependency, base::Closure())); - - internal::WorkerPoolTask::TaskVector tasks; - for (int i = 0; i < count; ++i) { - internal::WorkerPoolTask::TaskVector dependencies(1, dependency_task); - tasks.push_back(new FakeTaskImpl(callback, reply, &dependencies)); - } - scoped_refptr<FakeTaskImpl> completion_task( - new FakeTaskImpl(base::Bind(&FakeWorkerPool::OnTasksCompleted, - base::Unretained(this)), - base::Closure(), - &tasks)); - - scheduled_tasks_completion_.reset(new CompletionEvent); - - TaskGraph graph; - BuildTaskGraph(completion_task.get(), &graph); - WorkerPool::SetTaskGraph(&graph); - root_.swap(completion_task); - } - - void WaitForTasksToComplete() { - DCHECK(scheduled_tasks_completion_); - scheduled_tasks_completion_->Wait(); - } - - private: - void OnTasksCompleted() { - DCHECK(scheduled_tasks_completion_); - scheduled_tasks_completion_->Signal(); - } - - scoped_refptr<FakeTaskImpl> root_; - scoped_ptr<CompletionEvent> scheduled_tasks_completion_; -}; - -class WorkerPoolTest : public testing::Test { - public: - WorkerPoolTest() {} - virtual ~WorkerPoolTest() {} - - // Overridden from testing::Test: - virtual void SetUp() OVERRIDE { - Reset(); - } - virtual void TearDown() OVERRIDE { - worker_pool_->Shutdown(); - } - - void Reset() { - worker_pool_ = FakeWorkerPool::Create(); - } - - void RunAllTasksAndReset() { - worker_pool_->WaitForTasksToComplete(); - worker_pool_->Shutdown(); - worker_pool_->CheckForCompletedTasks(); - Reset(); - } - - FakeWorkerPool* worker_pool() { - return worker_pool_.get(); - } - - void RunTask(unsigned id) { - run_task_ids_.push_back(id); - } - - void OnTaskCompleted(unsigned id) { - on_task_completed_ids_.push_back(id); - } - - const std::vector<unsigned>& run_task_ids() { - return run_task_ids_; - } - - const std::vector<unsigned>& on_task_completed_ids() { - return on_task_completed_ids_; - } - - private: - scoped_ptr<FakeWorkerPool> worker_pool_; - std::vector<unsigned> run_task_ids_; - std::vector<unsigned> on_task_completed_ids_; -}; - -TEST_F(WorkerPoolTest, Basic) { - EXPECT_EQ(0u, run_task_ids().size()); - EXPECT_EQ(0u, on_task_completed_ids().size()); - - worker_pool()->ScheduleTasks( - base::Bind(&WorkerPoolTest::RunTask, base::Unretained(this), 0u), - base::Bind(&WorkerPoolTest::OnTaskCompleted, base::Unretained(this), 0u), - base::Closure(), - 1); - RunAllTasksAndReset(); - - EXPECT_EQ(1u, run_task_ids().size()); - EXPECT_EQ(1u, on_task_completed_ids().size()); - - worker_pool()->ScheduleTasks( - base::Bind(&WorkerPoolTest::RunTask, base::Unretained(this), 0u), - base::Bind(&WorkerPoolTest::OnTaskCompleted, base::Unretained(this), 0u), - base::Closure(), - 2); - RunAllTasksAndReset(); - - EXPECT_EQ(3u, run_task_ids().size()); - EXPECT_EQ(3u, on_task_completed_ids().size()); -} - -TEST_F(WorkerPoolTest, Dependencies) { - worker_pool()->ScheduleTasks( - base::Bind(&WorkerPoolTest::RunTask, base::Unretained(this), 1u), - base::Bind(&WorkerPoolTest::OnTaskCompleted, base::Unretained(this), 1u), - base::Bind(&WorkerPoolTest::RunTask, base::Unretained(this), 0u), - 1); - RunAllTasksAndReset(); - - // Check if dependency ran before task. - ASSERT_EQ(2u, run_task_ids().size()); - EXPECT_EQ(0u, run_task_ids()[0]); - EXPECT_EQ(1u, run_task_ids()[1]); - ASSERT_EQ(1u, on_task_completed_ids().size()); - EXPECT_EQ(1u, on_task_completed_ids()[0]); - - worker_pool()->ScheduleTasks( - base::Bind(&WorkerPoolTest::RunTask, base::Unretained(this), 1u), - base::Bind(&WorkerPoolTest::OnTaskCompleted, base::Unretained(this), 1u), - base::Bind(&WorkerPoolTest::RunTask, base::Unretained(this), 0u), - 2); - RunAllTasksAndReset(); - - // Dependency should only run once. - ASSERT_EQ(5u, run_task_ids().size()); - EXPECT_EQ(0u, run_task_ids()[2]); - EXPECT_EQ(1u, run_task_ids()[3]); - EXPECT_EQ(1u, run_task_ids()[4]); - ASSERT_EQ(3u, on_task_completed_ids().size()); - EXPECT_EQ(1u, on_task_completed_ids()[1]); - EXPECT_EQ(1u, on_task_completed_ids()[2]); -} - -} // namespace - -} // namespace cc |