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