Blender  V2.93
BLI_task.h
Go to the documentation of this file.
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 /* Use a define instead of `#pragma once` because of `bmesh_iterators_inline.h` */
18 #ifndef __BLI_TASK_H__
19 #define __BLI_TASK_H__
20 
21 #include <string.h> /* for memset() */
22 
23 struct ListBase;
24 
29 #include "BLI_threads.h"
30 #include "BLI_utildefines.h"
31 
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 
36 struct BLI_mempool;
37 
38 /* Task Scheduler
39  *
40  * Central scheduler that holds running threads ready to execute tasks. A single
41  * queue holds the task from all pools.
42  *
43  * Init/exit must be called before/after any task pools are created/freed, and
44  * must be called from the main threads. All other scheduler and pool functions
45  * are thread-safe. */
46 
47 void BLI_task_scheduler_init(void);
48 void BLI_task_scheduler_exit(void);
50 
51 /* Task Pool
52  *
53  * Pool of tasks that will be executed by the central task scheduler. For each
54  * pool, we can wait for all tasks to be done, or cancel them before they are
55  * done.
56  *
57  * Running tasks may spawn new tasks.
58  *
59  * Pools may be nested, i.e. a thread running a task can create another task
60  * pool with smaller tasks. When other threads are busy they will continue
61  * working on their own tasks, if not they will join in, no new threads will
62  * be launched.
63  */
64 
65 typedef enum TaskPriority {
69 
70 typedef struct TaskPool TaskPool;
71 typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata);
72 typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata);
73 
74 /* Regular task pool that immediately starts executing tasks as soon as they
75  * are pushed, either on the current or another thread. */
77 
78 /* Background: always run tasks in a background thread, never immediately
79  * execute them. For running background jobs. */
81 
82 /* Background Serial: run tasks one after the other in the background,
83  * without parallelization between the tasks. */
85 
86 /* Suspended: don't execute tasks until work_and_wait is called. This is slower
87  * as threads can't immediately start working. But it can be used if the data
88  * structures the threads operate on are not fully initialized until all tasks
89  * are created. */
91 
92 /* No threads: immediately executes tasks on the same thread. For debugging. */
94 
96 
98  TaskRunFunction run,
99  void *taskdata,
100  bool free_taskdata,
101  TaskFreeFunction freedata);
102 
103 /* work and wait until all tasks are done */
105 /* cancel all tasks, keep worker threads running */
107 
108 /* for worker threads, test if current task pool canceled. this function may
109  * only be called from worker threads and pool must be the task pool that the
110  * thread is currently executing a task from. */
112 
113 /* optional userdata pointer to pass along to run function */
115 
116 /* optional mutex to use from run function */
118 
119 /* Parallel for routines */
120 
121 /* Per-thread specific data passed to the callback. */
122 typedef struct TaskParallelTLS {
123  /* Copy of user-specifier chunk, which is copied from original chunk to all
124  * worker threads. This is similar to OpenMP's firstprivate.
125  */
128 
129 typedef void (*TaskParallelRangeFunc)(void *__restrict userdata,
130  const int iter,
131  const TaskParallelTLS *__restrict tls);
132 typedef void (*TaskParallelReduceFunc)(const void *__restrict userdata,
133  void *__restrict chunk_join,
134  void *__restrict chunk);
135 
136 typedef void (*TaskParallelFreeFunc)(const void *__restrict userdata, void *__restrict chunk);
137 
138 typedef struct TaskParallelSettings {
139  /* Whether caller allows to do threading of the particular range.
140  * Usually set by some equation, which forces threading off when threading
141  * overhead becomes higher than speed benefit.
142  * BLI_task_parallel_range() by itself will always use threading when range
143  * is higher than a chunk size. As in, threading will always be performed.
144  */
146  /* Each instance of looping chunks will get a copy of this data
147  * (similar to OpenMP's firstprivate).
148  */
149  void *userdata_chunk; /* Pointer to actual data. */
150  size_t userdata_chunk_size; /* Size of that data. */
151  /* Function called from calling thread once whole range have been
152  * processed.
153  */
154  /* Function called to join user data chunk into another, to reduce
155  * the result to the original userdata_chunk memory.
156  * The reduce functions should have no side effects, so that they
157  * can be run on any thread. */
159  /* Function called to free data created by TaskParallelRangeFunc. */
161  /* Minimum allowed number of range iterators to be handled by a single
162  * thread. This allows to achieve following:
163  * - Reduce amount of threading overhead.
164  * - Partially occupy thread pool with ranges which are computationally
165  * expensive, but which are smaller than amount of available threads.
166  * For example, it's possible to multi-thread [0 .. 64] range into 4
167  * thread which will be doing 16 iterators each.
168  * This is a preferred way to tell scheduler when to start threading than
169  * having a global use_threading switch based on just range size.
170  */
173 
175 
176 void BLI_task_parallel_range(const int start,
177  const int stop,
178  void *userdata,
180  const TaskParallelSettings *settings);
181 
182 /* This data is shared between all tasks, its access needs thread lock or similar protection.
183  */
185  /* Maximum amount of items to acquire at once. */
187  /* Next item to be acquired. */
188  void *next_item;
189  /* Index of the next item to be acquired. */
191  /* Indicates that end of iteration has been reached. */
193  /* Helper lock to protect access to this data in iterator getter callback,
194  * can be ignored (if the callback implements its own protection system, using atomics e.g.).
195  * Will be NULL when iterator is actually processed in a single thread. */
198 
199 typedef void (*TaskParallelIteratorIterFunc)(void *__restrict userdata,
200  const TaskParallelTLS *__restrict tls,
201  void **r_next_item,
202  int *r_next_index,
203  bool *r_do_abort);
204 
205 typedef void (*TaskParallelIteratorFunc)(void *__restrict userdata,
206  void *item,
207  int index,
208  const TaskParallelTLS *__restrict tls);
209 
210 void BLI_task_parallel_iterator(void *userdata,
212  void *init_item,
213  const int init_index,
214  const int tot_items,
216  const TaskParallelSettings *settings);
217 
218 void BLI_task_parallel_listbase(struct ListBase *listbase,
219  void *userdata,
221  const TaskParallelSettings *settings);
222 
223 typedef struct MempoolIterData MempoolIterData;
224 typedef void (*TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter);
225 void BLI_task_parallel_mempool(struct BLI_mempool *mempool,
226  void *userdata,
228  const bool use_threading);
229 
230 /* TODO(sergey): Think of a better place for this. */
232 {
233  memset(settings, 0, sizeof(*settings));
234  settings->use_threading = true;
235  /* Use default heuristic to define actual chunk size. */
236  settings->min_iter_per_thread = 0;
237 }
238 
239 /* Don't use this, store any thread specific data in tls->userdata_chunk instead.
240  * Only here for code to be removed. */
242 
243 /* Task Graph Scheduling */
244 /* Task Graphs can be used to create a forest of directional trees and schedule work to any tree.
245  * The nodes in the graph can be run in separate threads.
246  *
247  * +---- [root] ----+
248  * | |
249  * v v
250  * [node_1] +---- [node_2] ----+
251  * | |
252  * v v
253  * [node_3] [node_4]
254  *
255  * TaskGraph *task_graph = BLI_task_graph_create();
256  * TaskNode *root = BLI_task_graph_node_create(task_graph, root_exec, NULL, NULL);
257  * TaskNode *node_1 = BLI_task_graph_node_create(task_graph, node_exec, NULL, NULL);
258  * TaskNode *node_2 = BLI_task_graph_node_create(task_graph, node_exec, NULL, NULL);
259  * TaskNode *node_3 = BLI_task_graph_node_create(task_graph, node_exec, NULL, NULL);
260  * TaskNode *node_4 = BLI_task_graph_node_create(task_graph, node_exec, NULL, NULL);
261  *
262  * BLI_task_graph_edge_create(root, node_1);
263  * BLI_task_graph_edge_create(root, node_2);
264  * BLI_task_graph_edge_create(node_2, node_3);
265  * BLI_task_graph_edge_create(node_2, node_4);
266  *
267  * Any node can be triggered to start a chain of tasks. Normally you would trigger a root node but
268  * it is supported to start the chain of tasks anywhere in the forest or tree. When a node
269  * completes, the execution flow is forwarded via the created edges.
270  * When a child node has multiple parents the child node will be triggered once for each parent.
271  *
272  * BLI_task_graph_node_push_work(root);
273  *
274  * In this example After `root` is finished, `node_1` and `node_2` will be started.
275  * Only after `node_2` is finished `node_3` and `node_4` will be started.
276  *
277  * After scheduling work we need to wait until all the tasks have been finished.
278  *
279  * BLI_task_graph_work_and_wait();
280  *
281  * When finished you can clean up all the resources by freeing the task_graph. Nodes are owned by
282  * the graph and are freed task_data will only be freed if a free_func was given.
283  *
284  * BLI_task_graph_free(task_graph);
285  *
286  * Work can enter a tree on any node. Normally this would be the root_node.
287  * A `task_graph` can be reused, but the caller needs to make sure the task_data is reset.
288  *
289  * ** Task-Data **
290  *
291  * Typically you want give a task data to work on.
292  * Task data can be shared with other nodes, but be careful not to free the data multiple times.
293  * Task data is freed when calling `BLI_task_graph_free`.
294  *
295  * MyData *task_data = MEM_callocN(sizeof(MyData), __func__);
296  * TaskNode *root = BLI_task_graph_node_create(task_graph, root_exec, task_data, MEM_freeN);
297  * TaskNode *node_1 = BLI_task_graph_node_create(task_graph, node_exec, task_data, NULL);
298  * TaskNode *node_2 = BLI_task_graph_node_create(task_graph, node_exec, task_data, NULL);
299  * TaskNode *node_3 = BLI_task_graph_node_create(task_graph, node_exec, task_data, NULL);
300  * TaskNode *node_4 = BLI_task_graph_node_create(task_graph, node_exec, task_data, NULL);
301  *
302  */
303 struct TaskGraph;
304 struct TaskNode;
305 
306 typedef void (*TaskGraphNodeRunFunction)(void *__restrict task_data);
307 typedef void (*TaskGraphNodeFreeFunction)(void *task_data);
308 
309 struct TaskGraph *BLI_task_graph_create(void);
310 void BLI_task_graph_work_and_wait(struct TaskGraph *task_graph);
311 void BLI_task_graph_free(struct TaskGraph *task_graph);
312 struct TaskNode *BLI_task_graph_node_create(struct TaskGraph *task_graph,
314  void *user_data,
316 bool BLI_task_graph_node_push_work(struct TaskNode *task_node);
317 void BLI_task_graph_edge_create(struct TaskNode *from_node, struct TaskNode *to_node);
318 
319 #ifdef __cplusplus
320 }
321 #endif
322 
323 #endif
#define BLI_INLINE
void BLI_task_scheduler_init(void)
int BLI_task_scheduler_num_threads(void)
void(* TaskParallelReduceFunc)(const void *__restrict userdata, void *__restrict chunk_join, void *__restrict chunk)
Definition: BLI_task.h:132
TaskPool * BLI_task_pool_create_background(void *userdata, TaskPriority priority)
Definition: task_pool.cc:423
void(* TaskParallelRangeFunc)(void *__restrict userdata, const int iter, const TaskParallelTLS *__restrict tls)
Definition: BLI_task.h:129
void BLI_task_parallel_range(const int start, const int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition: task_range.cc:110
void(* TaskGraphNodeFreeFunction)(void *task_data)
Definition: BLI_task.h:307
void BLI_task_graph_edge_create(struct TaskNode *from_node, struct TaskNode *to_node)
Definition: task_graph.cc:153
struct MempoolIterData MempoolIterData
Definition: BLI_task.h:223
void * BLI_task_pool_user_data(TaskPool *pool)
Definition: task_pool.cc:541
struct TaskGraph * BLI_task_graph_create(void)
Definition: task_graph.cc:112
bool BLI_task_pool_current_canceled(TaskPool *pool)
Definition: task_pool.cc:526
void BLI_task_pool_work_and_wait(TaskPool *pool)
Definition: task_pool.cc:496
struct TaskParallelTLS TaskParallelTLS
void BLI_task_pool_cancel(TaskPool *pool)
Definition: task_pool.cc:511
TaskPool * BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority)
Definition: task_pool.cc:451
void(* TaskParallelIteratorIterFunc)(void *__restrict userdata, const TaskParallelTLS *__restrict tls, void **r_next_item, int *r_next_index, bool *r_do_abort)
Definition: BLI_task.h:199
void BLI_task_scheduler_exit(void)
bool BLI_task_graph_node_push_work(struct TaskNode *task_node)
Definition: task_graph.cc:141
void(* TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter)
Definition: BLI_task.h:224
ThreadMutex * BLI_task_pool_user_mutex(TaskPool *pool)
Definition: task_pool.cc:546
struct TaskNode * BLI_task_graph_node_create(struct TaskGraph *task_graph, TaskGraphNodeRunFunction run, void *user_data, TaskGraphNodeFreeFunction free_func)
Definition: task_graph.cc:131
void BLI_task_graph_free(struct TaskGraph *task_graph)
Definition: task_graph.cc:117
int BLI_task_parallel_thread_id(const TaskParallelTLS *tls)
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition: BLI_task.h:231
void BLI_task_parallel_mempool(struct BLI_mempool *mempool, void *userdata, TaskParallelMempoolFunc func, const bool use_threading)
TaskPool * BLI_task_pool_create(void *userdata, TaskPriority priority)
Definition: task_pool.cc:406
void(* TaskParallelIteratorFunc)(void *__restrict userdata, void *item, int index, const TaskParallelTLS *__restrict tls)
Definition: BLI_task.h:205
struct TaskParallelIteratorStateShared TaskParallelIteratorStateShared
void(* TaskGraphNodeRunFunction)(void *__restrict task_data)
Definition: BLI_task.h:306
void BLI_task_parallel_listbase(struct ListBase *listbase, void *userdata, TaskParallelIteratorFunc func, const TaskParallelSettings *settings)
TaskPool * BLI_task_pool_create_no_threads(void *userdata)
Definition: task_pool.cc:442
void BLI_task_parallel_iterator(void *userdata, TaskParallelIteratorIterFunc iter_func, void *init_item, const int init_index, const int tot_items, TaskParallelIteratorFunc func, const TaskParallelSettings *settings)
TaskPool * BLI_task_pool_create_suspended(void *userdata, TaskPriority priority)
Definition: task_pool.cc:433
void(* TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata)
Definition: BLI_task.h:72
void BLI_task_graph_work_and_wait(struct TaskGraph *task_graph)
Definition: task_graph.cc:122
void BLI_task_pool_free(TaskPool *pool)
Definition: task_pool.cc:456
void(* TaskParallelFreeFunc)(const void *__restrict userdata, void *__restrict chunk)
Definition: BLI_task.h:136
TaskPriority
Definition: BLI_task.h:65
@ TASK_PRIORITY_LOW
Definition: BLI_task.h:66
@ TASK_PRIORITY_HIGH
Definition: BLI_task.h:67
struct TaskParallelSettings TaskParallelSettings
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition: task_pool.cc:475
pthread_spinlock_t SpinLock
Definition: BLI_threads.h:111
pthread_mutex_t ThreadMutex
Definition: BLI_threads.h:83
static PyObject * free_func(PyObject *, PyObject *value)
void * user_data
void * task_data
Definition: task_graph.cc:57
TaskParallelReduceFunc func_reduce
Definition: BLI_task.h:158
TaskParallelFreeFunc func_free
Definition: BLI_task.h:160
size_t userdata_chunk_size
Definition: BLI_task.h:150
void * userdata_chunk
Definition: BLI_task.h:126
void * userdata
Definition: task_pool.cc:168
function< void(void)> TaskRunFunction
Definition: util_task.h:29