diff options
-rw-r--r-- | source/blender/blenlib/BLI_task.h | 1 | ||||
-rw-r--r-- | source/blender/blenlib/intern/task.c | 56 |
2 files changed, 56 insertions, 1 deletions
diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 3bf58a64307..81c277cd956 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -77,6 +77,7 @@ typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata, int t typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata, int threadid); TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata); +TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata); void BLI_task_pool_free(TaskPool *pool); void BLI_task_pool_push_ex( diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index 20ea5ec5ec8..3374a589fc3 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -61,12 +61,17 @@ struct TaskPool { ThreadMutex user_mutex; volatile bool do_cancel; + + /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler has to use its special + * background fallback thread in case we are in single-threaded situation. */ + bool run_in_background; }; struct TaskScheduler { pthread_t *threads; struct TaskThread *task_threads; int num_threads; + bool background_thread_only; ListBase queue; ThreadMutex queue_mutex; @@ -152,6 +157,11 @@ static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task current_task = current_task->next) { TaskPool *pool = current_task->pool; + + if (scheduler->background_thread_only && !pool->run_in_background) { + continue; + } + if (pool->num_threads == 0 || pool->currently_running_tasks < pool->num_threads) { @@ -216,6 +226,12 @@ TaskScheduler *BLI_task_scheduler_create(int num_threads) /* main thread will also work, so we count it too */ num_threads -= 1; + /* Add background-only thread if needed. */ + if (num_threads == 0) { + scheduler->background_thread_only = true; + num_threads = 1; + } + /* launch threads that will be waiting for work */ if (num_threads > 0) { int i; @@ -326,15 +342,28 @@ static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool) /* Task Pool */ -TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata) +static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, void *userdata, const bool is_background) { TaskPool *pool = MEM_callocN(sizeof(TaskPool), "TaskPool"); +#ifndef NDEBUG + /* Assert we do not try to create a background pool from some parent task - those only work OK from main thread. */ + if (is_background) { + const pthread_t thread_id = pthread_self(); + int i = scheduler->num_threads; + + while (i--) { + BLI_assert(scheduler->threads[i] != thread_id); + } + } +#endif + pool->scheduler = scheduler; pool->num = 0; pool->num_threads = 0; pool->currently_running_tasks = 0; pool->do_cancel = false; + pool->run_in_background = is_background; BLI_mutex_init(&pool->num_mutex); BLI_condition_init(&pool->num_cond); @@ -353,6 +382,31 @@ TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata) return pool; } +/** + * Create a normal task pool. + * This means that in single-threaded context, it will not be executed at all until you call + * \a BLI_task_pool_work_and_wait() on it. + */ +TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata) +{ + return task_pool_create_ex(scheduler, userdata, false); +} + +/** + * Create a background task pool. + * In multi-threaded context, there is no differences with \a BLI_task_pool_create(), but in single-threaded case + * it is ensured to have at least one worker thread to run on (i.e. you do not have to call + * \a BLI_task_pool_work_and_wait() on it to be sure it will be processed). + * + * \note Background pools are non-recursive (that is, you should not create other background pools in tasks assigned + * to a brackground pool, they could end never being executed, since the 'fallback' background thread is already + * busy with parent task in single-threaded context). + */ +TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata) +{ + return task_pool_create_ex(scheduler, userdata, true); +} + void BLI_task_pool_free(TaskPool *pool) { BLI_task_pool_stop(pool); |