1.. _work_isolation: 2 3Work Isolation 4============== 5 6 7.. container:: section 8 9 10 In |full_name|, a thread waiting for a 11 group of tasks to complete might execute other available tasks. In 12 particular, when a parallel construct calls another parallel 13 construct, a thread can obtain a task from the outer-level construct 14 while waiting for completion of the inner-level one. 15 16 17 In the following example with two ``parallel_for`` calls, the call to 18 the second (nested) parallel loop blocks execution of the first 19 (outer) loop iteration: 20 21 22 :: 23 24 25 // The first parallel loop. 26 oneapi::tbb::parallel_for( 0, N1, []( int i ) { 27 // The second parallel loop. 28 oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); 29 } ); 30 31 32 The blocked thread is allowed to take tasks belonging to the first 33 parallel loop. As a result, two or more iterations of the outer loop 34 might be simultaneously assigned to the same thread. In other words, 35 in oneTBB execution of functions constituting a parallel construct is 36 *unsequenced* even within a single thread. In most cases, this 37 behavior is harmless or even beneficial because it does not restrict 38 parallelism available for the thread. 39 40 41 However, in some cases such unsequenced execution may result in 42 errors. For example, a thread-local variable might unexpectedly 43 change its value after a nested parallel construct: 44 45 46 :: 47 48 49 oneapi::tbb::enumerable_thread_specific<int> ets; 50 oneapi::tbb::parallel_for( 0, N1, [&ets]( int i ) { 51 // Set a thread specific value 52 ets.local() = i; 53 oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); 54 // While executing the above parallel_for, the thread might have run iterations 55 // of the outer parallel_for, and so might have changed the thread specific value. 56 assert( ets.local()==i ); // The assertion may fail! 57 } ); 58 59 60 In other scenarios, the described behavior might lead to deadlocks 61 and other issues. In these cases, a stronger guarantee of execution 62 being sequenced within a thread is desired. For that, oneTBB provides 63 ways to *isolate* execution of a parallel construct, for its tasks to 64 not interfere with other simultaneously running tasks. 65 66 67 One of these ways is to execute the inner level loop in a separate 68 ``task_arena``: 69 70 71 :: 72 73 74 oneapi::tbb::enumerable_thread_specific<int> ets; 75 oneapi::tbb::task_arena nested; 76 oneapi::tbb::parallel_for( 0, N1, [&]( int i ) { 77 // Set a thread specific value 78 ets.local() = i; 79 nested.execute( []{ 80 // Run the inner parallel_for in a separate arena to prevent the thread 81 // from taking tasks of the outer parallel_for. 82 oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); 83 } ); 84 assert( ets.local()==i ); // Valid assertion 85 } ); 86 87 88 However, using a separate arena for work isolation is not always 89 convenient, and might have noticeable overheads. To address these 90 shortcomings, oneTBB provides ``this_task_arena::isolate`` function 91 which runs a user-provided functor in isolation by restricting the 92 calling thread to process only tasks scheduled in the scope of the 93 functor (also called the isolation region). 94 95 96 When entered a task waiting call or a blocking parallel construct 97 inside an isolated region, a thread can only execute tasks spawned 98 within the region and their child tasks spawned by other threads. The 99 thread is prohibited from executing any outer level tasks or tasks 100 belonging to other isolated regions. 101 102 103 The isolation region imposes restrictions only upon the thread that 104 called it. Other threads running in the same task arena have no 105 restrictions on task selection unless isolated by a distinct call to 106 ``this_task_arena::isolate``. 107 108 109 The following example demonstrates the use of 110 ``this_task_arena::isolate`` to ensure that a thread-local variable 111 is not changed unexpectedly during the call to a nested parallel 112 construct. 113 114 115 :: 116 117 118 #include "oneapi/tbb/task_arena.h" 119 #include "oneapi/tbb/parallel_for.h" 120 #include "oneapi/tbb/enumerable_thread_specific.h" 121 #include <cassert> 122 123 124 int main() { 125 const int N1 = 1000, N2 = 1000; 126 oneapi::tbb::enumerable_thread_specific<int> ets; 127 oneapi::tbb::parallel_for( 0, N1, [&ets]( int i ) { 128 // Set a thread specific value 129 ets.local() = i; 130 // Run the second parallel loop in an isolated region to prevent the current thread 131 // from taking tasks related to the outer parallel loop. 132 oneapi::tbb::this_task_arena::isolate( []{ 133 oneapi::tbb::parallel_for( 0, N2, []( int j ) { /* Some work */ } ); 134 } ); 135 assert( ets.local()==i ); // Valid assertion 136 } ); 137 return 0; 138 } 139 140