1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup bli
19 *
20 * Task graph.
21 */
22
23 #include "MEM_guardedalloc.h"
24
25 #include "BLI_task.h"
26
27 #include <memory>
28 #include <vector>
29
30 #ifdef WITH_TBB
31 /* Quiet top level deprecation message, unrelated to API usage here. */
32 # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
33 # include <tbb/flow_graph.h>
34 # include <tbb/tbb.h>
35 #endif
36
37 /* Task Graph */
38 struct TaskGraph {
39 #ifdef WITH_TBB
40 tbb::flow::graph tbb_graph;
41 #endif
42 std::vector<std::unique_ptr<TaskNode>> nodes;
43
44 #ifdef WITH_CXX_GUARDEDALLOC
45 MEM_CXX_CLASS_ALLOC_FUNCS("task_graph:TaskGraph")
46 #endif
47 };
48
49 /* TaskNode - a node in the task graph. */
50 struct TaskNode {
51 /* TBB Node. */
52 #ifdef WITH_TBB
53 tbb::flow::continue_node<tbb::flow::continue_msg> tbb_node;
54 #endif
55 /* Successors to execute after this task, for serial execution fallback. */
56 std::vector<TaskNode *> successors;
57
58 /* User function to be executed with given task data. */
59 TaskGraphNodeRunFunction run_func;
60 void *task_data;
61 /* Optional callback to free task data along with the graph. If task data
62 * is shared between nodes, only a single task node should free the data. */
63 TaskGraphNodeFreeFunction free_func;
64
TaskNodeTaskNode65 TaskNode(TaskGraph *task_graph,
66 TaskGraphNodeRunFunction run_func,
67 void *task_data,
68 TaskGraphNodeFreeFunction free_func)
69 :
70 #ifdef WITH_TBB
71 tbb_node(task_graph->tbb_graph,
72 tbb::flow::unlimited,
73 std::bind(&TaskNode::run, this, std::placeholders::_1)),
74 #endif
75 run_func(run_func),
76 task_data(task_data),
77 free_func(free_func)
78 {
79 #ifndef WITH_TBB
80 UNUSED_VARS(task_graph);
81 #endif
82 }
83
84 TaskNode(const TaskNode &other) = delete;
85 TaskNode &operator=(const TaskNode &other) = delete;
86
~TaskNodeTaskNode87 ~TaskNode()
88 {
89 if (task_data && free_func) {
90 free_func(task_data);
91 }
92 }
93
94 #ifdef WITH_TBB
runTaskNode95 tbb::flow::continue_msg run(const tbb::flow::continue_msg UNUSED(input))
96 {
97 tbb::this_task_arena::isolate([this] { run_func(task_data); });
98 return tbb::flow::continue_msg();
99 }
100 #endif
101
run_serialTaskNode102 void run_serial()
103 {
104 run_func(task_data);
105 for (TaskNode *successor : successors) {
106 successor->run_serial();
107 }
108 }
109
110 #ifdef WITH_CXX_GUARDEDALLOC
111 MEM_CXX_CLASS_ALLOC_FUNCS("task_graph:TaskNode")
112 #endif
113 };
114
BLI_task_graph_create(void)115 TaskGraph *BLI_task_graph_create(void)
116 {
117 return new TaskGraph();
118 }
119
BLI_task_graph_free(TaskGraph * task_graph)120 void BLI_task_graph_free(TaskGraph *task_graph)
121 {
122 delete task_graph;
123 }
124
BLI_task_graph_work_and_wait(TaskGraph * task_graph)125 void BLI_task_graph_work_and_wait(TaskGraph *task_graph)
126 {
127 #ifdef WITH_TBB
128 task_graph->tbb_graph.wait_for_all();
129 #else
130 UNUSED_VARS(task_graph);
131 #endif
132 }
133
BLI_task_graph_node_create(struct TaskGraph * task_graph,TaskGraphNodeRunFunction run,void * user_data,TaskGraphNodeFreeFunction free_func)134 struct TaskNode *BLI_task_graph_node_create(struct TaskGraph *task_graph,
135 TaskGraphNodeRunFunction run,
136 void *user_data,
137 TaskGraphNodeFreeFunction free_func)
138 {
139 TaskNode *task_node = new TaskNode(task_graph, run, user_data, free_func);
140 task_graph->nodes.push_back(std::unique_ptr<TaskNode>(task_node));
141 return task_node;
142 }
143
BLI_task_graph_node_push_work(struct TaskNode * task_node)144 bool BLI_task_graph_node_push_work(struct TaskNode *task_node)
145 {
146 #ifdef WITH_TBB
147 if (BLI_task_scheduler_num_threads() > 1) {
148 return task_node->tbb_node.try_put(tbb::flow::continue_msg());
149 }
150 #endif
151
152 task_node->run_serial();
153 return true;
154 }
155
BLI_task_graph_edge_create(struct TaskNode * from_node,struct TaskNode * to_node)156 void BLI_task_graph_edge_create(struct TaskNode *from_node, struct TaskNode *to_node)
157 {
158 #ifdef WITH_TBB
159 if (BLI_task_scheduler_num_threads() > 1) {
160 tbb::flow::make_edge(from_node->tbb_node, to_node->tbb_node);
161 return;
162 }
163 #endif
164
165 from_node->successors.push_back(to_node);
166 }
167