<divclass="textblock"><p>Real parallel workloads often require dynamic flow controls. Cpp-Taskflow supports an very efficient interface of conditional tasking for users to implement dynamic and cyclic control flows that are otherwise difficult to do with existing task programming frameworks.</p>
<p>A condition task evalutes a set of instructions and returns an integer index of the next immediate successor to execute. The index is defined with respect to the order of its successor construction.</p>
<p>The example above implements a simple yet commonly used feedback loop through a condition task (line 7-10) that returns a random binary value. If the return value from <code>cond</code> is 0, it loops back to itself, or otherwise to <code>stop</code>. Our conditional tasking interface is very neat and expressive. A complex flow control often just takes a few lines of code to implement. The code below creates another taskflow with three condition tasks.</p>
<p>You can use condition tasks to create cycles as long as the graph does not introduce task race during execution. However, cycles are not allowed in non-condition tasks.</p>
<p>In order to understand how an executor schedules condition tasks, we define two dependency types, <em>strong dependency</em> and <em>weak dependency</em>. A strong dependency is a preceding link from a non-condition task to another task. A weak dependency is a preceding link from a condition task to another task. The number of dependents of a task is the sum of strong dependency and weak dependency. The table below lists the strong dependency and weak dependency numbers of each task in the previous example.</p>
</div><p>You can query the number of strong dependents, the number of weak dependents, and the number of dependents of a task.</p>
<divclass="fragment"><divclass="line">1: <aclass="code" href="classtf_1_1Taskflow.html">tf::Taskflow</a> taskflow;</div><divclass="line">2: </div><divclass="line">3: <aclass="code" href="classtf_1_1Task.html">tf::Task</a> task = taskflow.<aclass="code" href="classtf_1_1FlowBuilder.html#a796e29175380f70246cf2a5639adc437">emplace</a>([](){});</div><divclass="line">4: </div><divclass="line">5: <spanclass="comment">// ... add more tasks and preceding links</span></div><divclass="line">6:</div><divclass="line">7: <aclass="codeRef" doxygen="/Users/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << task.<aclass="code" href="classtf_1_1Task.html#a974dc1d738b62b829ad261beeafbd67c">num_dependents</a>() << <spanclass="charliteral">'\n'</span>;</div><divclass="line">8: <aclass="codeRef" doxygen="/Users/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << task.<aclass="code" href="classtf_1_1Task.html#ad49a92e8858c3c298bed0215e341b66b">num_strong_dependents</a>() << <spanclass="charliteral">'\n'</span>; </div><divclass="line">9: <aclass="codeRef" doxygen="/Users/twhuang/PhD/Code/cpp-taskflow/doxygen/cppreference-doxygen-web.tag.xml:http://en.cppreference.com/w/" href="http://en.cppreference.com/w/cpp/io/basic_ostream.html">std::cout</a> << task.<aclass="code" href="classtf_1_1Task.html#af3bf886291af7f39957d43d17083fe07">num_weak_dependents</a>() << <spanclass="charliteral">'\n'</span>;</div></div><!-- fragment --><p>When you submit a task to an executor, the scheduler starts with tasks of <em>zero dependents</em> (both zero strong and weak dependencies) and continues to execute successive tasks whenever their <em>strong dependencies</em> are met. However, the scheduler skips this rule when executing a condition task and jumps directly to its successors indexed by the return value.</p>
<p>Each task has an <em>atomic</em> join counter to keep track of strong dependents that are met at runtime. When a task completes, the join counter is restored to the task's strong dependency number in the graph, such that the subsequent execution can reuse the counter again.</p>
<h1><aclass="anchor" id="C4_CommonPitfalls"></a>
Common Pitfalls</h1>
<p>Condition tasks are handy in creasing dynamic and cyclic control flows, but they are also easy to make mistakes. It is your responsibility to ensure a taskflow is properly conditioned. Top things to avoid include <em>no source tasks</em> to start with and <em>task race</em>. The figure below shows common pitfalls and their remedies.</p>
<p>In the error1 scenario, there is no source task for the scheduler to start with, and the simplest fix is to add a task S that has no dependents. In the error2 scenario, D might be scheduled twice by E through the strong dependency and C through the weak dependency (on return 1). To fix this problem, you can add an auxiliary task D-aux to break the mixed use of strong dependency and weak dependency. In the risky scenario, task X may not be raced if P and M is exclusively branching to X.</p>
<p>A good practice for avoiding mistakes of conditional tasking is to infer the execution flow of your graphs based on our scheduling rules. Make sure there is no task race. </p>
</div></div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<divid="nav-path" class="navpath"><!-- id is needed for treeview function! -->