[Bf-blender-cvs] [04ac876] master: BLI_task: add support for full-background taskpools.

Bastien Montagne noreply at git.blender.org
Mon Nov 2 17:07:25 CET 2015


Commit: 04ac8768efc342997f941f08688398c1d90bec79
Author: Bastien Montagne
Date:   Mon Nov 2 16:57:48 2015 +0100
Branches: master
https://developer.blender.org/rB04ac8768efc342997f941f08688398c1d90bec79

BLI_task: add support for full-background taskpools.

With current code, in single-threaded context, a pool of task may never be executed
until one calls BLI_task_pool_work_and_wait() on it, this is not acceptable for
asynchronous tasks where you never want to actually lock the main thread.

This commits adds an extra thread in single-threaded case, and a new 'type' of pool,
such that one can create real background pools of tasks. See code for details.

Review: D1565

===================================================================

M	source/blender/blenlib/BLI_task.h
M	source/blender/blenlib/intern/task.c

===================================================================

diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h
index 3bf58a6..81c277c 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 20ea5ec..3374a58 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);




More information about the Bf-blender-cvs mailing list