Index: cc/resources/task_graph_runner.h |
diff --git a/cc/resources/task_graph_runner.h b/cc/resources/task_graph_runner.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6915c5bb2785cc36c2db2f88aa40e76e1ecde94a |
--- /dev/null |
+++ b/cc/resources/task_graph_runner.h |
@@ -0,0 +1,235 @@ |
+// 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_RESOURCES_TASK_GRAPH_RUNNER_H_ |
+#define CC_RESOURCES_TASK_GRAPH_RUNNER_H_ |
+ |
+#include <queue> |
+#include <string> |
+#include <vector> |
+ |
+#include "base/containers/scoped_ptr_hash_map.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/synchronization/condition_variable.h" |
+#include "base/threading/simple_thread.h" |
+#include "cc/base/cc_export.h" |
+#include "cc/base/scoped_ptr_deque.h" |
+ |
+namespace cc { |
+namespace internal { |
+ |
+class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> { |
+ public: |
+ typedef std::vector<scoped_refptr<Task> > Vector; |
+ |
+ virtual void RunOnWorkerThread(unsigned thread_index) = 0; |
+ |
+ void DidSchedule(); |
+ void WillRun(); |
+ void DidRun(); |
+ |
+ bool HasFinishedRunning() const; |
+ |
+ protected: |
+ friend class base::RefCountedThreadSafe<Task>; |
+ |
+ Task(); |
+ virtual ~Task(); |
+ |
+ bool did_schedule_; |
+ bool did_run_; |
+}; |
+ |
+} // namespace internal |
+} // namespace cc |
+ |
+#if defined(COMPILER_GCC) |
+namespace BASE_HASH_NAMESPACE { |
+template <> struct hash<cc::internal::Task*> { |
+ size_t operator()(cc::internal::Task* ptr) const { |
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr)); |
+ } |
+}; |
+} // namespace BASE_HASH_NAMESPACE |
+#endif // COMPILER |
+ |
+namespace cc { |
+namespace internal { |
+ |
+// Dependencies are represented by edges in a task graph. A graph node |
+// store edges as a vector of dependents. Each graph node is assigned |
+// a priority and a run count that matches the number of dependencies. |
+class CC_EXPORT GraphNode { |
+ public: |
+ typedef std::vector<GraphNode*> Vector; |
+ typedef base::ScopedPtrHashMap<Task*, GraphNode> Map; |
+ |
+ GraphNode(Task* task, unsigned priority); |
+ ~GraphNode(); |
+ |
+ Task* task() { return task_; } |
+ |
+ void add_dependent(GraphNode* dependent) { |
+ DCHECK(dependent); |
+ dependents_.push_back(dependent); |
+ } |
+ const Vector& dependents() const { return dependents_; } |
+ |
+ unsigned priority() const { return priority_; } |
+ |
+ unsigned num_dependencies() const { return num_dependencies_; } |
+ void add_dependency() { ++num_dependencies_; } |
+ void remove_dependency() { |
+ DCHECK(num_dependencies_); |
+ --num_dependencies_; |
+ } |
+ |
+ private: |
+ Task* task_; |
+ Vector dependents_; |
+ unsigned priority_; |
+ unsigned num_dependencies_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GraphNode); |
+}; |
+ |
+class TaskGraphRunner; |
+ |
+// Opaque identifier that defines a namespace of tasks. |
+class CC_EXPORT NamespaceToken { |
+ public: |
+ NamespaceToken() : id_(0) {} |
+ ~NamespaceToken() {} |
+ |
+ bool IsValid() const { |
+ return id_ != 0; |
+ } |
+ |
+ private: |
+ friend class TaskGraphRunner; |
+ |
+ explicit NamespaceToken(int id) : id_(id) {} |
+ |
+ int id_; |
+}; |
+ |
+// A worker thread pool that runs tasks provided by task graph. Destructor |
+// might block and should not be used on a thread that needs to be responsive. |
+class CC_EXPORT TaskGraphRunner : public base::DelegateSimpleThread::Delegate { |
+ public: |
+ typedef GraphNode::Map TaskGraph; |
+ |
+ TaskGraphRunner(size_t num_threads, const std::string& thread_name_prefix); |
+ virtual ~TaskGraphRunner(); |
+ |
+ // Returns a unique token that can be used to pass a task graph to |
+ // SetTaskGraph(). Valid tokens are always nonzero. |
+ NamespaceToken GetNamespaceToken(); |
+ |
+ // 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 it later get canceled by another |
+ // call to SetTaskGraph(). |
+ void SetTaskGraph(NamespaceToken token, TaskGraph* graph); |
+ |
+ // Wait for all scheduled tasks to finish running. |
+ void WaitForTasksToFinishRunning(NamespaceToken token); |
+ |
+ // Collect all completed tasks in |completed_tasks|. |
+ void CollectCompletedTasks( |
+ NamespaceToken token, Task::Vector* completed_tasks); |
+ |
+ private: |
+ struct TaskNamespace { |
+ typedef std::vector<TaskNamespace*> Vector; |
+ |
+ TaskNamespace(); |
+ ~TaskNamespace(); |
+ |
+ // This set contains all pending tasks. |
+ TaskGraph pending_tasks; |
+ // This set contains all currently running tasks. |
+ TaskGraph running_tasks; |
+ // Completed tasks not yet collected by origin thread. |
+ Task::Vector completed_tasks; |
+ // Ordered set of tasks that are ready to run. |
+ internal::GraphNode::Vector ready_to_run_tasks; |
+ }; |
+ |
+ typedef base::ScopedPtrHashMap<int, TaskNamespace> TaskNamespaceMap; |
+ |
+ static bool CompareTaskPriority(const internal::GraphNode* a, |
+ const internal::GraphNode* b) { |
+ // In this system, numerically lower priority is run first. |
+ if (a->priority() != b->priority()) |
+ return a->priority() > b->priority(); |
+ |
+ // Run task with most dependents first when priority is the same. |
+ return a->dependents().size() < b->dependents().size(); |
+ } |
+ |
+ static bool CompareTaskNamespacePriority(const TaskNamespace* a, |
+ const TaskNamespace* b) { |
+ DCHECK(!a->ready_to_run_tasks.empty()); |
+ DCHECK(!b->ready_to_run_tasks.empty()); |
+ |
+ // Compare based on task priority of the ready_to_run_tasks heap |
+ // .front() will hold the max element of the heap, |
+ // except after pop_heap, when max element is moved to .back(). |
+ return CompareTaskPriority(a->ready_to_run_tasks.front(), |
+ b->ready_to_run_tasks.front()); |
+ } |
+ |
+ static bool HasFinishedRunningTasksInNamespace( |
+ TaskNamespace* task_namespace) { |
+ return task_namespace->pending_tasks.empty() && |
+ task_namespace->running_tasks.empty(); |
+ } |
+ |
+ // Overridden from base::DelegateSimpleThread: |
+ virtual void Run() OVERRIDE; |
+ |
+ // This lock protects all members of this class. 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_; |
+ |
+ // Condition variable that is waited on by origin threads until a |
+ // namespace has finished running all associated tasks. |
+ base::ConditionVariable has_namespaces_with_finished_running_tasks_cv_; |
+ |
+ // Provides a unique id to each NamespaceToken. |
+ int next_namespace_id_; |
+ |
+ // This set contains all namespaces with pending, running or completed |
+ // tasks not yet collected. |
+ TaskNamespaceMap namespaces_; |
+ |
+ // Ordered set of task namespaces that have ready to run tasks. |
+ TaskNamespace::Vector ready_to_run_namespaces_; |
+ |
+ // 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_; |
+ |
+ ScopedPtrDeque<base::DelegateSimpleThread> workers_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TaskGraphRunner); |
+}; |
+ |
+} // namespace internal |
+} // namespace cc |
+ |
+#endif // CC_RESOURCES_TASK_GRAPH_RUNNER_H_ |