[Bf-blender-cvs] [ed1fc9d96bb] master: BLI: support disabling task isolation in task pool

Jacques Lucke noreply at git.blender.org
Tue Jun 8 10:41:45 CEST 2021


Commit: ed1fc9d96bbac2ac3d4282400a9717e19e84211d
Author: Jacques Lucke
Date:   Tue Jun 8 09:37:45 2021 +0200
Branches: master
https://developer.blender.org/rBed1fc9d96bbac2ac3d4282400a9717e19e84211d

BLI: support disabling task isolation in task pool

Under some circumstances using task isolation can cause deadlocks.
Previously, our task pool implementation would run all tasks in an
isolated region. Now using task isolation is optional and can be
turned on/off for individual task pools.

Task pools that spawn new tasks recursively should never enable
task isolation. There is a new check that finds these cases at runtime.
Right now this check is disabled, so that this commit is a pure refactor.
It will be enabled in an upcoming commit.

This fixes T88598.

Differential Revision: https://developer.blender.org/D11415

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

M	source/blender/blenkernel/intern/editmesh_tangent.c
M	source/blender/blenkernel/intern/lib_override.c
M	source/blender/blenkernel/intern/mesh_evaluate.c
M	source/blender/blenkernel/intern/mesh_tangent.c
M	source/blender/blenkernel/intern/ocean.c
M	source/blender/blenkernel/intern/particle.c
M	source/blender/blenkernel/intern/particle_distribute.c
M	source/blender/blenlib/BLI_task.h
M	source/blender/blenlib/intern/task_iterator.c
M	source/blender/blenlib/intern/task_pool.cc
M	source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
M	source/blender/compositor/intern/COM_WorkScheduler.cc
M	source/blender/depsgraph/intern/eval/deg_eval.cc
M	source/blender/editors/mesh/editmesh_undo.c
M	source/blender/editors/render/render_opengl.c
M	source/blender/editors/sculpt_paint/paint_image_proj.c
M	source/blender/editors/space_clip/clip_editor.c
M	source/blender/editors/space_clip/clip_ops.c
M	source/blender/editors/space_file/filelist.c
M	source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
M	source/blender/imbuf/intern/imageprocess.c

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

diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c
index d849f4ab37d..088a2087a96 100644
--- a/source/blender/blenkernel/intern/editmesh_tangent.c
+++ b/source/blender/blenkernel/intern/editmesh_tangent.c
@@ -362,7 +362,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
     /* Calculation */
     if (em->tottri != 0) {
       TaskPool *task_pool;
-      task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
+      task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
 
       tangent_mask_curr = 0;
       /* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index c93971e7b11..6b2ffa3b944 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -2335,7 +2335,8 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
   }
 
   struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false};
-  TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
+  TaskPool *task_pool = BLI_task_pool_create(
+      &create_pool_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
 
   FOREACH_MAIN_ID_BEGIN (bmain, id) {
     if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index 616ec79c099..345546bc9cf 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1715,7 +1715,8 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
     loop_split_generator(NULL, &common_data);
   }
   else {
-    TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH);
+    TaskPool *task_pool = BLI_task_pool_create(
+        &common_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
 
     loop_split_generator(task_pool, &common_data);
 
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c
index 2e22e521a13..6a7ff0851f5 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.c
@@ -656,7 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
 
     /* Calculation */
     if (looptri_len != 0) {
-      TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
+      TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
 
       tangent_mask_curr = 0;
       /* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 9d53dad8d03..9b9ed0adcf4 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -663,7 +663,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount
   osd.scale = scale;
   osd.chop_amount = chop_amount;
 
-  pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH);
+  pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
 
   BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
 
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index a873ecec6f1..3ae5d039125 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -3179,7 +3179,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
     return;
   }
 
-  task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
+  task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
   totchild = ctx.totchild;
   totparent = ctx.totparent;
 
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index ad617b4198b..6cae6cd6fa2 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -1330,7 +1330,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
     return;
   }
 
-  task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
+  task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
 
   totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
   psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);
diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h
index 9e61686b37a..339bb256819 100644
--- a/source/blender/blenlib/BLI_task.h
+++ b/source/blender/blenlib/BLI_task.h
@@ -67,17 +67,55 @@ typedef enum TaskPriority {
   TASK_PRIORITY_HIGH,
 } TaskPriority;
 
+/**
+ * Task isolation helps avoid unexpected task scheduling decisions that can lead to bugs if wrong
+ * assumptions were made. Typically that happens when doing "nested threading", i.e. one thread
+ * schedules a bunch of main-tasks and those spawn new subtasks.
+ *
+ * What can happen is that when a main-task waits for its subtasks to complete on other threads,
+ * another main-task is scheduled within the already running main-task. Generally, this is good,
+ * because it leads to better performance. However, sometimes code (often unintentionally) makes
+ * the assumption that at most one main-task runs on a thread at a time.
+ *
+ * The bugs often show themselves in two ways:
+ * - Deadlock, when a main-task holds a mutex while waiting for its subtasks to complete.
+ * - Data corruption, when a main-task makes wrong assumptions about a threadlocal variable.
+ *
+ * Task isolation can avoid these bugs by making sure that a main-task does not start executing
+ * another main-task while waiting for its subtasks. More precisely, a function that runs in an
+ * isolated region is only allowed to run subtasks that were spawned in the same isolated region.
+ *
+ * Unfortunately, incorrect use of task isolation can lead to deadlocks itself. This can happen
+ * when threading primitives are used that separate spawning tasks from executing them. The problem
+ * occurs when a task is spawned in one isolated region while the tasks are waited for in another
+ * isolated region. In this setup, the thread that is waiting for the spawned tasks to complete
+ * cannot run the tasks itself. On a single thread, that causes a deadlock already. When there are
+ * multiple threads, another thread will typically run the task and avoid the deadlock. However, if
+ * this situation happens on all threads at the same time, all threads will deadlock. This happened
+ * in T88598.
+ */
+typedef enum TaskIsolation {
+  /* Do not use task isolation. Always use this when tasks are pushed recursively. */
+  TASK_ISOLATION_OFF,
+  /* Run each task in its own isolated region. */
+  TASK_ISOLATION_ON,
+} TaskIsolation;
+
 typedef struct TaskPool TaskPool;
 typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata);
 typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata);
 
 /* Regular task pool that immediately starts executing tasks as soon as they
  * are pushed, either on the current or another thread. */
-TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create(void *userdata,
+                               TaskPriority priority,
+                               TaskIsolation task_isolation);
 
 /* Background: always run tasks in a background thread, never immediately
  * execute them. For running background jobs. */
-TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_background(void *userdata,
+                                          TaskPriority priority,
+                                          TaskIsolation task_isolation);
 
 /* Background Serial: run tasks one after the other in the background,
  * without parallelization between the tasks. */
@@ -87,7 +125,9 @@ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority pr
  * as threads can't immediately start working. But it can be used if the data
  * structures the threads operate on are not fully initialized until all tasks
  * are created. */
-TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_suspended(void *userdata,
+                                         TaskPriority priority,
+                                         TaskIsolation task_isolation);
 
 /* No threads: immediately executes tasks on the same thread. For debugging. */
 TaskPool *BLI_task_pool_create_no_threads(void *userdata);
diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c
index 38271e5823f..85cd9718ed4 100644
--- a/source/blender/blenlib/intern/task_iterator.c
+++ b/source/blender/blenlib/intern/task_iterator.c
@@ -223,7 +223,7 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings,
   void *userdata_chunk_array = NULL;
   const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
 
-  TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH);
+  TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
 
   if (use_userdata_chunk) {
     userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks);
@@ -398,7 +398,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
     return;
   }
 
-  task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH);
+  task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
   num_threads = BLI_task_scheduler_num_threads();
 
   /* The idea here is to prevent creating task for each of the loop iterations
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index 6404f5264cc..d72674c1c00 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -22,6 +22,7 @@
 
 #include <cstdlib>
 #include <memory>
+#include <thread>
 #include <utility>
 
 #include "MEM_guardedalloc.h"
@@ -111,15 +112,7 @@ class Task {

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list