5a5fc8d5b7e32f872ea8d56559f9b2a7920b67ad
[blender.git] / source / blender / blenlib / intern / task.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/blenlib/intern/task.c
22  *  \ingroup bli
23  *
24  * A generic task system which can be used for any task based subsystem.
25  */
26
27 #include <stdlib.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_listbase.h"
32 #include "BLI_math.h"
33 #include "BLI_task.h"
34 #include "BLI_threads.h"
35
36 #include "atomic_ops.h"
37
38 /* Types */
39
40 typedef struct Task {
41         struct Task *next, *prev;
42
43         TaskRunFunction run;
44         void *taskdata;
45         bool free_taskdata;
46         TaskFreeFunction freedata;
47         TaskPool *pool;
48 } Task;
49
50 struct TaskPool {
51         TaskScheduler *scheduler;
52
53         volatile size_t num;
54         volatile size_t done;
55         size_t num_threads;
56         size_t currently_running_tasks;
57         ThreadMutex num_mutex;
58         ThreadCondition num_cond;
59
60         void *userdata;
61         ThreadMutex user_mutex;
62
63         volatile bool do_cancel;
64
65         /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler has to use its special
66          * background fallback thread in case we are in single-threaded situation. */
67         bool run_in_background;
68 };
69
70 struct TaskScheduler {
71         pthread_t *threads;
72         struct TaskThread *task_threads;
73         int num_threads;
74         bool background_thread_only;
75
76         ListBase queue;
77         ThreadMutex queue_mutex;
78         ThreadCondition queue_cond;
79
80         volatile bool do_exit;
81 };
82
83 typedef struct TaskThread {
84         TaskScheduler *scheduler;
85         int id;
86 } TaskThread;
87
88 /* Helper */
89 static void task_data_free(Task *task, const int thread_id)
90 {
91         if (task->free_taskdata) {
92                 if (task->freedata) {
93                         task->freedata(task->pool, task->taskdata, thread_id);
94                 }
95                 else {
96                         MEM_freeN(task->taskdata);
97                 }
98         }
99 }
100
101 /* Task Scheduler */
102
103 static void task_pool_num_decrease(TaskPool *pool, size_t done)
104 {
105         BLI_mutex_lock(&pool->num_mutex);
106
107         BLI_assert(pool->num >= done);
108
109         pool->num -= done;
110         atomic_sub_z(&pool->currently_running_tasks, done);
111         pool->done += done;
112
113         if (pool->num == 0)
114                 BLI_condition_notify_all(&pool->num_cond);
115
116         BLI_mutex_unlock(&pool->num_mutex);
117 }
118
119 static void task_pool_num_increase(TaskPool *pool)
120 {
121         BLI_mutex_lock(&pool->num_mutex);
122
123         pool->num++;
124         BLI_condition_notify_all(&pool->num_cond);
125
126         BLI_mutex_unlock(&pool->num_mutex);
127 }
128
129 static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task)
130 {
131         bool found_task = false;
132         BLI_mutex_lock(&scheduler->queue_mutex);
133
134         while (!scheduler->queue.first && !scheduler->do_exit)
135                 BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
136
137         do {
138                 Task *current_task;
139
140                 /* Assuming we can only have a void queue in 'exit' case here seems logical (we should only be here after
141                  * our worker thread has been woken up from a condition_wait(), which only happens after a new task was
142                  * added to the queue), but it is wrong.
143                  * Waiting on condition may wake up the thread even if condition is not signaled (spurious wake-ups), and some
144                  * race condition may also empty the queue **after** condition has been signaled, but **before** awoken thread
145                  * reaches this point...
146                  * See http://stackoverflow.com/questions/8594591
147                  *
148                  * So we only abort here if do_exit is set.
149                  */
150                 if (scheduler->do_exit) {
151                         BLI_mutex_unlock(&scheduler->queue_mutex);
152                         return false;
153                 }
154
155                 for (current_task = scheduler->queue.first;
156                      current_task != NULL;
157                      current_task = current_task->next)
158                 {
159                         TaskPool *pool = current_task->pool;
160
161                         if (scheduler->background_thread_only && !pool->run_in_background) {
162                                 continue;
163                         }
164
165                         if (pool->num_threads == 0 ||
166                             pool->currently_running_tasks < pool->num_threads)
167                         {
168                                 *task = current_task;
169                                 found_task = true;
170                                 atomic_add_z(&pool->currently_running_tasks, 1);
171                                 BLI_remlink(&scheduler->queue, *task);
172                                 break;
173                         }
174                 }
175                 if (!found_task)
176                         BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex);
177         } while (!found_task);
178
179         BLI_mutex_unlock(&scheduler->queue_mutex);
180
181         return true;
182 }
183
184 static void *task_scheduler_thread_run(void *thread_p)
185 {
186         TaskThread *thread = (TaskThread *) thread_p;
187         TaskScheduler *scheduler = thread->scheduler;
188         int thread_id = thread->id;
189         Task *task;
190
191         /* keep popping off tasks */
192         while (task_scheduler_thread_wait_pop(scheduler, &task)) {
193                 TaskPool *pool = task->pool;
194
195                 /* run task */
196                 task->run(pool, task->taskdata, thread_id);
197
198                 /* delete task */
199                 task_data_free(task, thread_id);
200                 MEM_freeN(task);
201
202                 /* notify pool task was done */
203                 task_pool_num_decrease(pool, 1);
204         }
205
206         return NULL;
207 }
208
209 TaskScheduler *BLI_task_scheduler_create(int num_threads)
210 {
211         TaskScheduler *scheduler = MEM_callocN(sizeof(TaskScheduler), "TaskScheduler");
212
213         /* multiple places can use this task scheduler, sharing the same
214          * threads, so we keep track of the number of users. */
215         scheduler->do_exit = false;
216
217         BLI_listbase_clear(&scheduler->queue);
218         BLI_mutex_init(&scheduler->queue_mutex);
219         BLI_condition_init(&scheduler->queue_cond);
220
221         if (num_threads == 0) {
222                 /* automatic number of threads will be main thread + num cores */
223                 num_threads = BLI_system_thread_count();
224         }
225
226         /* main thread will also work, so we count it too */
227         num_threads -= 1;
228
229         /* Add background-only thread if needed. */
230         if (num_threads == 0) {
231             scheduler->background_thread_only = true;
232             num_threads = 1;
233         }
234
235         /* launch threads that will be waiting for work */
236         if (num_threads > 0) {
237                 int i;
238
239                 scheduler->num_threads = num_threads;
240                 scheduler->threads = MEM_callocN(sizeof(pthread_t) * num_threads, "TaskScheduler threads");
241                 scheduler->task_threads = MEM_callocN(sizeof(TaskThread) * num_threads, "TaskScheduler task threads");
242
243                 for (i = 0; i < num_threads; i++) {
244                         TaskThread *thread = &scheduler->task_threads[i];
245                         thread->scheduler = scheduler;
246                         thread->id = i + 1;
247
248                         if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) {
249                                 fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads);
250                         }
251                 }
252         }
253
254         return scheduler;
255 }
256
257 void BLI_task_scheduler_free(TaskScheduler *scheduler)
258 {
259         Task *task;
260
261         /* stop all waiting threads */
262         BLI_mutex_lock(&scheduler->queue_mutex);
263         scheduler->do_exit = true;
264         BLI_condition_notify_all(&scheduler->queue_cond);
265         BLI_mutex_unlock(&scheduler->queue_mutex);
266
267         /* delete threads */
268         if (scheduler->threads) {
269                 int i;
270
271                 for (i = 0; i < scheduler->num_threads; i++) {
272                         if (pthread_join(scheduler->threads[i], NULL) != 0)
273                                 fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads);
274                 }
275
276                 MEM_freeN(scheduler->threads);
277         }
278
279         /* Delete task thread data */
280         if (scheduler->task_threads) {
281                 MEM_freeN(scheduler->task_threads);
282         }
283
284         /* delete leftover tasks */
285         for (task = scheduler->queue.first; task; task = task->next) {
286                 task_data_free(task, 0);
287         }
288         BLI_freelistN(&scheduler->queue);
289
290         /* delete mutex/condition */
291         BLI_mutex_end(&scheduler->queue_mutex);
292         BLI_condition_end(&scheduler->queue_cond);
293
294         MEM_freeN(scheduler);
295 }
296
297 int BLI_task_scheduler_num_threads(TaskScheduler *scheduler)
298 {
299         return scheduler->num_threads + 1;
300 }
301
302 static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority)
303 {
304         task_pool_num_increase(task->pool);
305
306         /* add task to queue */
307         BLI_mutex_lock(&scheduler->queue_mutex);
308
309         if (priority == TASK_PRIORITY_HIGH)
310                 BLI_addhead(&scheduler->queue, task);
311         else
312                 BLI_addtail(&scheduler->queue, task);
313
314         BLI_condition_notify_one(&scheduler->queue_cond);
315         BLI_mutex_unlock(&scheduler->queue_mutex);
316 }
317
318 static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool)
319 {
320         Task *task, *nexttask;
321         size_t done = 0;
322
323         BLI_mutex_lock(&scheduler->queue_mutex);
324
325         /* free all tasks from this pool from the queue */
326         for (task = scheduler->queue.first; task; task = nexttask) {
327                 nexttask = task->next;
328
329                 if (task->pool == pool) {
330                         task_data_free(task, 0);
331                         BLI_freelinkN(&scheduler->queue, task);
332
333                         done++;
334                 }
335         }
336
337         BLI_mutex_unlock(&scheduler->queue_mutex);
338
339         /* notify done */
340         task_pool_num_decrease(pool, done);
341 }
342
343 /* Task Pool */
344
345 static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, void *userdata, const bool is_background)
346 {
347         TaskPool *pool = MEM_callocN(sizeof(TaskPool), "TaskPool");
348
349 #ifndef NDEBUG
350         /* Assert we do not try to create a background pool from some parent task - those only work OK from main thread. */
351         if (is_background) {
352                 const pthread_t thread_id = pthread_self();
353                 int i = scheduler->num_threads;
354
355                 while (i--) {
356                         BLI_assert(!pthread_equal(scheduler->threads[i], thread_id));
357                 }
358         }
359 #endif
360
361         pool->scheduler = scheduler;
362         pool->num = 0;
363         pool->num_threads = 0;
364         pool->currently_running_tasks = 0;
365         pool->do_cancel = false;
366         pool->run_in_background = is_background;
367
368         BLI_mutex_init(&pool->num_mutex);
369         BLI_condition_init(&pool->num_cond);
370
371         pool->userdata = userdata;
372         BLI_mutex_init(&pool->user_mutex);
373
374         /* Ensure malloc will go fine from threads,
375          *
376          * This is needed because we could be in main thread here
377          * and malloc could be non-threda safe at this point because
378          * no other jobs are running.
379          */
380         BLI_begin_threaded_malloc();
381
382         return pool;
383 }
384
385 /**
386  * Create a normal task pool.
387  * This means that in single-threaded context, it will not be executed at all until you call
388  * \a BLI_task_pool_work_and_wait() on it.
389  */
390 TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata)
391 {
392         return task_pool_create_ex(scheduler, userdata, false);
393 }
394
395 /**
396  * Create a background task pool.
397  * In multi-threaded context, there is no differences with \a BLI_task_pool_create(), but in single-threaded case
398  * it is ensured to have at least one worker thread to run on (i.e. you do not have to call
399  * \a BLI_task_pool_work_and_wait() on it to be sure it will be processed).
400  *
401  * \note Background pools are non-recursive (that is, you should not create other background pools in tasks assigned
402  *       to a background pool, they could end never being executed, since the 'fallback' background thread is already
403  *       busy with parent task in single-threaded context).
404  */
405 TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata)
406 {
407         return task_pool_create_ex(scheduler, userdata, true);
408 }
409
410 void BLI_task_pool_free(TaskPool *pool)
411 {
412         BLI_task_pool_stop(pool);
413
414         BLI_mutex_end(&pool->num_mutex);
415         BLI_condition_end(&pool->num_cond);
416
417         BLI_mutex_end(&pool->user_mutex);
418
419         MEM_freeN(pool);
420
421         BLI_end_threaded_malloc();
422 }
423
424 void BLI_task_pool_push_ex(
425         TaskPool *pool, TaskRunFunction run, void *taskdata,
426         bool free_taskdata, TaskFreeFunction freedata, TaskPriority priority)
427 {
428         Task *task = MEM_callocN(sizeof(Task), "Task");
429
430         task->run = run;
431         task->taskdata = taskdata;
432         task->free_taskdata = free_taskdata;
433         task->freedata = freedata;
434         task->pool = pool;
435
436         task_scheduler_push(pool->scheduler, task, priority);
437 }
438
439 void BLI_task_pool_push(
440         TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskPriority priority)
441 {
442         BLI_task_pool_push_ex(pool, run, taskdata, free_taskdata, NULL, priority);
443 }
444
445 void BLI_task_pool_work_and_wait(TaskPool *pool)
446 {
447         TaskScheduler *scheduler = pool->scheduler;
448
449         BLI_mutex_lock(&pool->num_mutex);
450
451         while (pool->num != 0) {
452                 Task *task, *work_task = NULL;
453                 bool found_task = false;
454
455                 BLI_mutex_unlock(&pool->num_mutex);
456
457                 BLI_mutex_lock(&scheduler->queue_mutex);
458
459                 /* find task from this pool. if we get a task from another pool,
460                  * we can get into deadlock */
461
462                 if (pool->num_threads == 0 ||
463                     pool->currently_running_tasks < pool->num_threads)
464                 {
465                         for (task = scheduler->queue.first; task; task = task->next) {
466                                 if (task->pool == pool) {
467                                         work_task = task;
468                                         found_task = true;
469                                         BLI_remlink(&scheduler->queue, task);
470                                         break;
471                                 }
472                         }
473                 }
474
475                 BLI_mutex_unlock(&scheduler->queue_mutex);
476
477                 /* if found task, do it, otherwise wait until other tasks are done */
478                 if (found_task) {
479                         /* run task */
480                         atomic_add_z(&pool->currently_running_tasks, 1);
481                         work_task->run(pool, work_task->taskdata, 0);
482
483                         /* delete task */
484                         task_data_free(task, 0);
485                         MEM_freeN(work_task);
486
487                         /* notify pool task was done */
488                         task_pool_num_decrease(pool, 1);
489                 }
490
491                 BLI_mutex_lock(&pool->num_mutex);
492                 if (pool->num == 0)
493                         break;
494
495                 if (!found_task)
496                         BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
497         }
498
499         BLI_mutex_unlock(&pool->num_mutex);
500 }
501
502 int BLI_pool_get_num_threads(TaskPool *pool)
503 {
504         if (pool->num_threads != 0) {
505                 return pool->num_threads;
506         }
507         else {
508                 return BLI_task_scheduler_num_threads(pool->scheduler);
509         }
510 }
511
512 void BLI_pool_set_num_threads(TaskPool *pool, int num_threads)
513 {
514         /* NOTE: Don't try to modify threads while tasks are running! */
515         pool->num_threads = num_threads;
516 }
517
518 void BLI_task_pool_cancel(TaskPool *pool)
519 {
520         pool->do_cancel = true;
521
522         task_scheduler_clear(pool->scheduler, pool);
523
524         /* wait until all entries are cleared */
525         BLI_mutex_lock(&pool->num_mutex);
526         while (pool->num)
527                 BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
528         BLI_mutex_unlock(&pool->num_mutex);
529
530         pool->do_cancel = false;
531 }
532
533 void BLI_task_pool_stop(TaskPool *pool)
534 {
535         task_scheduler_clear(pool->scheduler, pool);
536
537         BLI_assert(pool->num == 0);
538 }
539
540 bool BLI_task_pool_canceled(TaskPool *pool)
541 {
542         return pool->do_cancel;
543 }
544
545 void *BLI_task_pool_userdata(TaskPool *pool)
546 {
547         return pool->userdata;
548 }
549
550 ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool)
551 {
552         return &pool->user_mutex;
553 }
554
555 size_t BLI_task_pool_tasks_done(TaskPool *pool)
556 {
557         return pool->done;
558 }
559
560 /* Parallel range routines */
561
562 /**
563  *
564  * Main functions:
565  * - #BLI_task_parallel_range
566  *
567  * TODO:
568  * - #BLI_task_parallel_foreach_listbase (#ListBase - double linked list)
569  * - #BLI_task_parallel_foreach_link (#Link - single linked list)
570  * - #BLI_task_parallel_foreach_ghash/gset (#GHash/#GSet - hash & set)
571  * - #BLI_task_parallel_foreach_mempool (#BLI_mempool - iterate over mempools)
572  *
573  * Possible improvements:
574  *
575  * - Chunk iterations to reduce number of spin locks.
576  */
577
578 /* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */
579 #define MALLOCA(_size) ((_size) <= 8192) ? alloca((_size)) : MEM_mallocN((_size), __func__)
580 #define MALLOCA_FREE(_mem, _size) if (((_mem) != NULL) && ((_size) > 8192)) MEM_freeN((_mem))
581
582 typedef struct ParallelRangeState {
583         int start, stop;
584         void *userdata;
585         void *userdata_chunk;
586         size_t userdata_chunk_size;
587
588         TaskParallelRangeFunc func;
589         TaskParallelRangeFuncEx func_ex;
590
591         int iter;
592         int chunk_size;
593         SpinLock lock;
594 } ParallelRangeState;
595
596 BLI_INLINE bool parallel_range_next_iter_get(
597         ParallelRangeState * __restrict state,
598         int * __restrict iter, int * __restrict count)
599 {
600         bool result = false;
601         BLI_spin_lock(&state->lock);
602         if (state->iter < state->stop) {
603                 *count = min_ii(state->chunk_size, state->stop - state->iter);
604                 *iter = state->iter;
605                 state->iter += *count;
606                 result = true;
607         }
608         BLI_spin_unlock(&state->lock);
609         return result;
610 }
611
612 static void parallel_range_func(
613         TaskPool * __restrict pool,
614         void *UNUSED(taskdata),
615         int threadid)
616 {
617         ParallelRangeState * __restrict state = BLI_task_pool_userdata(pool);
618         int iter, count;
619
620         const bool use_userdata_chunk = (state->func_ex != NULL) &&
621                                         (state->userdata_chunk_size != 0) && (state->userdata_chunk != NULL);
622         void *userdata_chunk = use_userdata_chunk ? MALLOCA(state->userdata_chunk_size) : NULL;
623
624         while (parallel_range_next_iter_get(state, &iter, &count)) {
625                 int i;
626
627                 if (state->func_ex) {
628                         if (use_userdata_chunk) {
629                                 memcpy(userdata_chunk, state->userdata_chunk, state->userdata_chunk_size);
630                         }
631
632                         for (i = 0; i < count; ++i) {
633                                 state->func_ex(state->userdata, userdata_chunk, iter + i, threadid);
634                         }
635                 }
636                 else {
637                         for (i = 0; i < count; ++i) {
638                                 state->func(state->userdata, iter + i);
639                         }
640                 }
641         }
642
643         MALLOCA_FREE(userdata_chunk, state->userdata_chunk_size);
644 }
645
646 /**
647  * This function allows to parallelized for loops in a similar way to OpenMP's 'parallel for' statement.
648  *
649  * See public API doc for description of parameters.
650  */
651 static void task_parallel_range_ex(
652         int start, int stop,
653         void *userdata,
654         void *userdata_chunk,
655         const size_t userdata_chunk_size,
656         TaskParallelRangeFunc func,
657         TaskParallelRangeFuncEx func_ex,
658         const bool use_threading,
659         const bool use_dynamic_scheduling)
660 {
661         TaskScheduler *task_scheduler;
662         TaskPool *task_pool;
663         ParallelRangeState state;
664         int i, num_threads, num_tasks;
665
666         if (start == stop) {
667                 return;
668         }
669
670         BLI_assert(start < stop);
671         if (userdata_chunk_size != 0) {
672                 BLI_assert(func_ex != NULL && func == NULL);
673                 BLI_assert(userdata_chunk != NULL);
674         }
675
676         /* If it's not enough data to be crunched, don't bother with tasks at all,
677          * do everything from the main thread.
678          */
679         if (!use_threading) {
680                 if (func_ex) {
681                         const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
682                         void *userdata_chunk_local = NULL;
683
684                         if (use_userdata_chunk) {
685                                 userdata_chunk_local = MALLOCA(userdata_chunk_size);
686                                 memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
687                         }
688
689                         for (i = start; i < stop; ++i) {
690                                 func_ex(userdata, userdata_chunk, i, 0);
691                         }
692
693                         MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
694                 }
695                 else {
696                         for (i = start; i < stop; ++i) {
697                                 func(userdata, i);
698                         }
699                 }
700
701                 return;
702         }
703
704         task_scheduler = BLI_task_scheduler_get();
705         task_pool = BLI_task_pool_create(task_scheduler, &state);
706         num_threads = BLI_task_scheduler_num_threads(task_scheduler);
707
708         /* The idea here is to prevent creating task for each of the loop iterations
709          * and instead have tasks which are evenly distributed across CPU cores and
710          * pull next iter to be crunched using the queue.
711          */
712         num_tasks = num_threads * 2;
713
714         BLI_spin_init(&state.lock);
715         state.start = start;
716         state.stop = stop;
717         state.userdata = userdata;
718         state.userdata_chunk = userdata_chunk;
719         state.userdata_chunk_size = userdata_chunk_size;
720         state.func = func;
721         state.func_ex = func_ex;
722         state.iter = start;
723         if (use_dynamic_scheduling) {
724                 state.chunk_size = 32;
725         }
726         else {
727                 state.chunk_size = max_ii(1, (stop - start) / (num_tasks));
728         }
729
730         num_tasks = max_ii(1, (stop - start) / state.chunk_size);
731
732         for (i = 0; i < num_tasks; i++) {
733                 BLI_task_pool_push(task_pool,
734                                    parallel_range_func,
735                                    NULL, false,
736                                    TASK_PRIORITY_HIGH);
737         }
738
739         BLI_task_pool_work_and_wait(task_pool);
740         BLI_task_pool_free(task_pool);
741
742         BLI_spin_end(&state.lock);
743 }
744
745 /**
746  * This function allows to parallelized for loops in a similar way to OpenMP's 'parallel for' statement.
747  *
748  * \param start First index to process.
749  * \param stop Index to stop looping (excluded).
750  * \param userdata Common userdata passed to all instances of \a func.
751  * \param userdata_chunk Optional, each instance of looping chunks will get a copy of this data
752  *                       (similar to OpenMP's firstprivate).
753  * \param userdata_chunk_size Memory size of \a userdata_chunk.
754  * \param func_ex Callback function (advanced version).
755  * \param use_threading If \a true, actually split-execute loop in threads, else just do a sequential forloop
756  *                      (allows caller to use any kind of test to switch on parallelization or not).
757  * \param use_dynamic_scheduling If \a true, the whole range is divided in a lot of small chunks (of size 32 currently),
758  *                               otherwise whole range is split in a few big chunks (num_threads * 2 chunks currently).
759  */
760 void BLI_task_parallel_range_ex(
761         int start, int stop,
762         void *userdata,
763         void *userdata_chunk,
764         const size_t userdata_chunk_size,
765         TaskParallelRangeFuncEx func_ex,
766         const bool use_threading,
767         const bool use_dynamic_scheduling)
768 {
769         task_parallel_range_ex(
770                     start, stop, userdata, userdata_chunk, userdata_chunk_size, NULL, func_ex,
771                     use_threading, use_dynamic_scheduling);
772 }
773
774 /**
775  * A simpler version of \a BLI_task_parallel_range_ex, which does not use \a use_dynamic_scheduling,
776  * and does not handle 'firstprivate'-like \a userdata_chunk.
777  *
778  * \param start First index to process.
779  * \param stop Index to stop looping (excluded).
780  * \param userdata Common userdata passed to all instances of \a func.
781  * \param func Callback function (simple version).
782  * \param use_threading If \a true, actually split-execute loop in threads, else just do a sequential forloop
783  *                      (allows caller to use any kind of test to switch on parallelization or not).
784  */
785 void BLI_task_parallel_range(
786         int start, int stop,
787         void *userdata,
788         TaskParallelRangeFunc func,
789                 const bool use_threading)
790 {
791         task_parallel_range_ex(start, stop, userdata, NULL, 0, func, NULL, use_threading, false);
792 }
793
794 #undef MALLOCA
795 #undef MALLOCA_FREE
796