1 #pragma once
2 
3 #include "task.hpp"
4 
5 namespace tf {
6 
7 /**
8 @class FlowBuilder
9 
10 @brief building methods of a task dependency graph
11 
12 */
13 class FlowBuilder {
14 
15   friend class Executor;
16 
17   public:
18 
19     /**
20     @brief creates a static task from a given callable object
21 
22     @tparam C callable type
23 
24     @param callable a callable object constructible from std::function<void()>
25 
26     @return a Task handle
27     */
28     template <typename C>
29     std::enable_if_t<is_static_task_v<C>, Task> emplace(C&& callable);
30 
31     /**
32     @brief creates a dynamic task from a given callable object
33 
34     @tparam C callable type
35 
36     @param callable a callable object constructible from std::function<void(Subflow&)>
37 
38     @return a Task handle
39     */
40     template <typename C>
41     std::enable_if_t<is_dynamic_task_v<C>, Task> emplace(C&& callable);
42 
43     /**
44     @brief creates a condition task from a given callable object
45 
46     @tparam C callable type
47 
48     @param callable a callable object constructible from std::function<int()>
49 
50     @return a Task handle
51     */
52     template <typename C>
53     std::enable_if_t<is_condition_task_v<C>, Task> emplace(C&& callable);
54 
55 #ifdef TF_ENABLE_CUDA
56     /**
57     @brief creates a cudaflow task from a given callable object
58 
59     @tparam C callable type
60 
61     @param callable a callable object constructible from std::function<void(cudaFlow&)>
62 
63     @return a Task handle
64     */
65     template <typename C>
66     std::enable_if_t<is_cudaflow_task_v<C>, Task> emplace(C&& callable);
67 #endif
68 
69     /**
70     @brief creates multiple tasks from a list of callable objects
71 
72     @tparam C... callable types
73 
74     @param callables one or multiple callable objects constructible from each task category
75 
76     @return a Task handle
77     */
78     template <typename... C, std::enable_if_t<(sizeof...(C)>1), void>* = nullptr>
79     auto emplace(C&&... callables);
80 
81     /**
82     @brief creates a module task from a taskflow
83 
84     @param taskflow a taskflow object for the module
85     @return a Task handle
86     */
87     Task composed_of(Taskflow& taskflow);
88 
89     /**
90     @brief creates an empty task
91 
92     @return a Task handle
93     */
94     Task placeholder();
95 
96     /**
97     @brief adds adjacent dependency links to a linear list of tasks
98 
99     @param tasks a vector of tasks
100     */
101     void linearize(std::vector<Task>& tasks);
102 
103     /**
104     @brief adds adjacent dependency links to a linear list of tasks
105 
106     @param tasks an initializer list of tasks
107     */
108     void linearize(std::initializer_list<Task> tasks);
109 
110     // ------------------------------------------------------------------------
111     // parallel iterations
112     // ------------------------------------------------------------------------
113 
114     /**
115     @brief constructs a STL-styled parallel-for task
116 
117     @tparam B beginning iterator type
118     @tparam E ending iterator type
119     @tparam C callable type
120 
121     @param first iterator to the beginning (inclusive)
122     @param last iterator to the end (exclusive)
123     @param callable a callable object to apply to the dereferenced iterator
124 
125     @return a Task handle
126 
127     The task spawns a subflow that applies the callable object to each object obtained by dereferencing every iterator in the range <tt>[first, last)</tt>. By default, we employ the guided partition algorithm with chunk size equal to one.
128 
129     This method is equivalent to the parallel execution of the following loop:
130 
131     @code{.cpp}
132     for(auto itr=first; itr!=last; itr++) {
133       callable(*itr);
134     }
135     @endcode
136 
137     Arguments templated to enable stateful passing using std::reference_wrapper.
138 
139     The callable needs to take a single argument of the dereferenced type.
140     */
141     template <typename B, typename E, typename C>
142     Task for_each(B&& first, E&& last, C&& callable);
143 
144     /**
145     @brief constructs a STL-styled parallel-for task using the guided partition algorithm
146 
147     @tparam B beginning iterator type
148     @tparam E ending iterator type
149     @tparam C callable type
150     @tparam H chunk size type
151 
152     @param beg iterator to the beginning (inclusive)
153     @param end iterator to the end (exclusive)
154     @param callable a callable object to apply to the dereferenced iterator
155     @param chunk_size chunk size
156 
157     @return a Task handle
158 
159     The task spawns a subflow that applies the callable object to each object obtained by dereferencing every iterator in the range <tt>[beg, end)</tt>. The runtime partitions the range into chunks of the given chunk size, where each chunk is processed by a worker.
160 
161     Arguments are templated to enable stateful passing using std::reference_wrapper.
162 
163     The callable needs to take a single argument of the dereferenced type.
164     */
165     template <typename B, typename E, typename C, typename H = size_t>
166     Task for_each_guided(B&& beg, E&& end, C&& callable, H&& chunk_size = 1);
167 
168     /**
169     @brief constructs a STL-styled parallel-for task using the dynamic partition algorithm
170 
171     @tparam B beginning iterator type
172     @tparam E ending iterator type
173     @tparam C callable type
174     @tparam H chunk size type
175 
176     @param beg iterator to the beginning (inclusive)
177     @param end iterator to the end (exclusive)
178     @param callable a callable object to apply to the dereferenced iterator
179     @param chunk_size chunk size
180 
181     @return a Task handle
182 
183     The task spawns a subflow that applies the callable object to each object obtained by dereferencing every iterator in the range <tt>[beg, end)</tt>. The runtime partitions the range into chunks of the given chunk size, where each chunk is processed by a worker.
184 
185     Arguments are templated to enable stateful passing using std::reference_wrapper.
186 
187     The callable needs to take a single argument of the dereferenced type.
188     */
189     template <typename B, typename E, typename C, typename H = size_t>
190     Task for_each_dynamic(B&& beg, E&& end, C&& callable, H&& chunk_size = 1);
191 
192     /**
193     @brief constructs a STL-styled parallel-for task using the dynamic partition algorithm
194 
195     @tparam B beginning iterator type
196     @tparam E ending iterator type
197     @tparam C callable type
198     @tparam H chunk size type
199 
200     @param beg iterator to the beginning (inclusive)
201     @param end iterator to the end (exclusive)
202     @param callable a callable object to apply to the dereferenced iterator
203     @param chunk_size chunk size
204 
205     @return a Task handle
206 
207     The task spawns a subflow that applies the callable object to each object obtained by dereferencing every iterator in the range <tt>[beg, end)</tt>. The runtime partitions the range into chunks of the given chunk size, where each chunk is processed by a worker. When the given chunk size is zero, the runtime distributes the work evenly across workers.
208 
209     Arguments are templated to enable stateful passing using std::reference_wrapper.
210 
211     The callable needs to take a single argument of the dereferenced type.
212     */
213     template <typename B, typename E, typename C, typename H = size_t>
214     Task for_each_static(
215       B&& beg, E&& end, C&& callable, H&& chunk_size = 0
216     );
217 
218     /**
219     @brief constructs an index-based parallel-for task
220 
221     @tparam B beginning index type (must be integral)
222     @tparam E ending index type (must be integral)
223     @tparam S step type (must be integral)
224     @tparam C callable type
225 
226     @param first index of the beginning (inclusive)
227     @param last index of the end (exclusive)
228     @param step step size
229     @param callable a callable object to apply to each valid index
230 
231     @return a Task handle
232 
233     The task spawns a subflow that applies the callable object to each index in the range <tt>[first, last)</tt> with the step size. By default, we employ the guided partition algorithm with chunk size equal to one.
234 
235     This method is equivalent to the parallel execution of the following loop:
236 
237     @code{.cpp}
238     // case 1: step size is positive
239     for(auto i=first; i<last; i+=step) {
240       callable(i);
241     }
242 
243     // case 2: step size is negative
244     for(auto i=first, i>last; i+=step) {
245       callable(i);
246     }
247     @endcode
248 
249     Arguments are templated to enable stateful passing using std::reference_wrapper.
250 
251     The callable needs to take a single argument of the index type.
252 
253     */
254     template <typename B, typename E, typename S, typename C>
255     Task for_each_index(B&& first, E&& last, S&& step, C&& callable);
256 
257     /**
258     @brief constructs an index-based parallel-for task using the guided partition algorithm.
259 
260     @tparam B beginning index type (must be integral)
261     @tparam E ending index type (must be integral)
262     @tparam S step type (must be integral)
263     @tparam C callable type
264     @tparam H chunk size type
265 
266     @param beg index of the beginning (inclusive)
267     @param end index of the end (exclusive)
268     @param step step size
269     @param callable a callable object to apply to each valid index
270     @param chunk_size chunk size (default 1)
271 
272     @return a Task handle
273 
274     The task spawns a subflow that applies the callable object to each index in the range <tt>[beg, end)</tt> with the step size. The runtime partitions the range into chunks of the given size, where each chunk is processed by a worker.
275 
276     Arguments are templated to enable stateful passing using std::reference_wrapper.
277 
278     The callable needs to take a single argument of the index type.
279     */
280     template <typename B, typename E, typename S, typename C, typename H = size_t>
281     Task for_each_index_guided(
282       B&& beg, E&& end, S&& step, C&& callable, H&& chunk_size = 1
283     );
284 
285     /**
286     @brief constructs an index-based parallel-for task using the dynamic partition algorithm.
287 
288     @tparam B beginning index type (must be integral)
289     @tparam E ending index type (must be integral)
290     @tparam S step type (must be integral)
291     @tparam C callable type
292     @tparam H chunk size type
293 
294     @param beg index of the beginning (inclusive)
295     @param end index of the end (exclusive)
296     @param step step size
297     @param callable a callable object to apply to each valid index
298     @param chunk_size chunk size (default 1)
299 
300     @return a Task handle
301 
302     The task spawns a subflow that applies the callable object to each index in the range <tt>[beg, end)</tt> with the step size. The runtime partitions the range into chunks of the given size, where each chunk is processed by a worker.
303 
304     Arguments are templated to enable stateful passing using std::reference_wrapper.
305 
306     The callable needs to take a single argument of the index type.
307     */
308     template <typename B, typename E, typename S, typename C, typename H = size_t>
309     Task for_each_index_dynamic(
310       B&& beg, E&& end, S&& step, C&& callable, H&& chunk_size = 1
311     );
312 
313     /**
314     @brief constructs an index-based parallel-for task using the static partition algorithm.
315 
316     @tparam B beginning index type (must be integral)
317     @tparam E ending index type (must be integral)
318     @tparam S step type (must be integral)
319     @tparam C callable type
320     @tparam H chunk size type
321 
322     @param beg index of the beginning (inclusive)
323     @param end index of the end (exclusive)
324     @param step step size
325     @param callable a callable object to apply to each valid index
326     @param chunk_size chunk size (default 0)
327 
328     @return a Task handle
329 
330     The task spawns a subflow that applies the callable object to each index in the range <tt>[beg, end)</tt> with the step size. The runtime partitions the range into chunks of the given size, where each chunk is processed by a worker. When the given chunk size is zero, the runtime distributes the work evenly across workers.
331 
332     Arguments are templated to enable stateful passing using std::reference_wrapper.
333 
334     The callable needs to take a single argument of the index type.
335     */
336     template <typename B, typename E, typename S, typename C, typename H = size_t>
337     Task for_each_index_static(
338       B&& beg, E&& end, S&& step, C&& callable, H&& chunk_size = 0
339     );
340 
341     // ------------------------------------------------------------------------
342     // reduction
343     // ------------------------------------------------------------------------
344 
345     /**
346     @brief constructs a STL-styled parallel-reduce task
347 
348     @tparam B beginning iterator type
349     @tparam E ending iterator type
350     @tparam T result type
351     @tparam O binary reducer type
352 
353     @param first iterator to the beginning (inclusive)
354     @param last iterator to the end (exclusive)
355     @param init initial value of the reduction and the storage for the reduced result
356     @param bop binary operator that will be applied
357 
358     @return a Task handle
359 
360     The task spawns a subflow to perform parallel reduction over @c init and the elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of the given chunk size, where each chunk is processed by a worker. By default, we employ the guided partition algorithm.
361 
362     This method is equivalent to the parallel execution of the following loop:
363 
364     @code{.cpp}
365     for(auto itr=first; itr!=last; itr++) {
366       init = bop(init, *itr);
367     }
368     @endcode
369 
370     Arguments are templated to enable stateful passing using std::reference_wrapper.
371     */
372     template <typename B, typename E, typename T, typename O>
373     Task reduce(B&& first, E&& last, T& init, O&& bop);
374 
375     /**
376     @brief constructs a STL-styled parallel-reduce task using the guided partition algorithm
377 
378     @tparam B beginning iterator type
379     @tparam E ending iterator type
380     @tparam T result type
381     @tparam O binary reducer type
382     @tparam H chunk size type
383 
384     @param first iterator to the beginning (inclusive)
385     @param last iterator to the end (exclusive)
386     @param init initial value of the reduction and the storage for the reduced result
387     @param bop binary operator that will be applied
388     @param chunk_size chunk size
389 
390     The task spawns a subflow to perform parallel reduction over @c init and the elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of size @c chunk_size, where each chunk is processed by a worker.
391 
392     Arguments are templated to enable stateful passing using std::reference_wrapper.
393 
394     @return a Task handle
395     */
396     template <typename B, typename E, typename T, typename O, typename H = size_t>
397     Task reduce_guided(
398       B&& first, E&& last, T& init, O&& bop, H&& chunk_size = 1
399     );
400 
401     /**
402     @brief constructs a STL-styled parallel-reduce task using the dynamic partition algorithm
403 
404     @tparam B beginning iterator type
405     @tparam E ending iterator type
406     @tparam T result type
407     @tparam O binary reducer type
408     @tparam H chunk size type
409 
410     @param first iterator to the beginning (inclusive)
411     @param last iterator to the end (exclusive)
412     @param init initial value of the reduction and the storage for the reduced result
413     @param bop binary operator that will be applied
414     @param chunk_size chunk size
415 
416     The task spawns a subflow to perform parallel reduction over @c init and the elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of size @c chunk_size, where each chunk is processed by a worker.
417 
418     Arguments are templated to enable stateful passing using std::reference_wrapper.
419 
420     @return a Task handle
421     */
422     template <typename B, typename E, typename T, typename O, typename H = size_t>
423     Task reduce_dynamic(
424       B&& first, E&& last, T& init, O&& bop, H&& chunk_size = 1
425     );
426 
427     /**
428     @brief constructs a STL-styled parallel-reduce task using the static partition algorithm
429 
430     @tparam B beginning iterator type
431     @tparam E ending iterator type
432     @tparam T result type
433     @tparam O binary reducer type
434     @tparam H chunk size type
435 
436     @param first iterator to the beginning (inclusive)
437     @param last iterator to the end (exclusive)
438     @param init initial value of the reduction and the storage for the reduced result
439     @param bop binary operator that will be applied
440     @param chunk_size chunk size
441 
442     The task spawns a subflow to perform parallel reduction over @c init and the elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of size @c chunk_size, where each chunk is processed by a worker.
443 
444     Arguments are templated to enable stateful passing using std::reference_wrapper.
445 
446     @return a Task handle
447     */
448     template <typename B, typename E, typename T, typename O, typename H = size_t>
449     Task reduce_static(
450       B&& first, E&& last, T& init, O&& bop, H&& chunk_size = 0
451     );
452 
453     // ------------------------------------------------------------------------
454     // transfrom and reduction
455     // ------------------------------------------------------------------------
456 
457     /**
458     @brief constructs a STL-styled parallel transform-reduce task
459 
460     @tparam B beginning iterator type
461     @tparam E ending iterator type
462     @tparam T result type
463     @tparam BOP binary reducer type
464     @tparam UOP unary transformion type
465 
466     @param first iterator to the beginning (inclusive)
467     @param last iterator to the end (exclusive)
468     @param init initial value of the reduction and the storage for the reduced result
469     @param bop binary operator that will be applied in unspecified order to the results of @c uop
470     @param uop unary operator that will be applied to transform each element in the range to the result type
471 
472     @return a Task handle
473 
474     The task spawns a subflow to perform parallel reduction over @c init and the transformed elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of the given chunk size, where each chunk is processed by a worker. By default, we employ the guided partition algorithm.
475 
476     This method is equivalent to the parallel execution of the following loop:
477 
478     @code{.cpp}
479     for(auto itr=first; itr!=last; itr++) {
480       init = bop(init, uop(*itr));
481     }
482     @endcode
483 
484     Arguments are templated to enable stateful passing using std::reference_wrapper.
485     */
486     template <typename B, typename E, typename T, typename BOP, typename UOP>
487     Task transform_reduce(B&& first, E&& last, T& init, BOP&& bop, UOP&& uop);
488 
489     /**
490     @brief constructs a STL-styled parallel transform-reduce task using the guided partition algorithm
491 
492     @tparam B beginning iterator type
493     @tparam E ending iterator type
494     @tparam T result type
495     @tparam BOP binary reducer type
496     @tparam UOP unary transformion type
497     @tparam H chunk size type
498 
499     @param first iterator to the beginning (inclusive)
500     @param last iterator to the end (exclusive)
501     @param init initial value of the reduction and the storage for the reduced result
502     @param bop binary operator that will be applied in unspecified order to the results of @c uop
503     @param uop unary operator that will be applied to transform each element in the range to the result type
504     @param chunk_size chunk size
505 
506     @return a Task handle
507 
508     The task spawns a subflow to perform parallel reduction over @c init and the transformed elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of size @c chunk_size, where each chunk is processed by a worker.
509 
510     Arguments are templated to enable stateful passing using std::reference_wrapper.
511     */
512     template <typename B, typename E, typename T, typename BOP, typename UOP, typename H = size_t>
513     Task transform_reduce_guided(
514       B&& first, E&& last, T& init, BOP&& bop, UOP&& uop, H&& chunk_size = 1
515     );
516 
517     /**
518     @brief constructs a STL-styled parallel transform-reduce task using the static partition algorithm
519 
520     @tparam B beginning iterator type
521     @tparam E ending iterator type
522     @tparam T result type
523     @tparam BOP binary reducer type
524     @tparam UOP unary transformion type
525     @tparam H chunk size type
526 
527     @param first iterator to the beginning (inclusive)
528     @param last iterator to the end (exclusive)
529     @param init initial value of the reduction and the storage for the reduced result
530     @param bop binary operator that will be applied in unspecified order to the results of @c uop
531     @param uop unary operator that will be applied to transform each element in the range to the result type
532     @param chunk_size chunk size
533 
534     @return a Task handle
535 
536     The task spawns a subflow to perform parallel reduction over @c init and the transformed elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of size @c chunk_size, where each chunk is processed by a worker.
537 
538     Arguments are templated to enable stateful passing using std::reference_wrapper.
539     */
540     template <typename B, typename E, typename T, typename BOP, typename UOP, typename H = size_t>
541     Task transform_reduce_static(
542       B&& first, E&& last, T& init, BOP&& bop, UOP&& uop, H&& chunk_size = 0
543     );
544 
545     /**
546     @brief constructs a STL-styled parallel transform-reduce task using the dynamic partition algorithm
547 
548     @tparam B beginning iterator type
549     @tparam E ending iterator type
550     @tparam T result type
551     @tparam BOP binary reducer type
552     @tparam UOP unary transformion type
553     @tparam H chunk size type
554 
555     @param first iterator to the beginning (inclusive)
556     @param last iterator to the end (exclusive)
557     @param init initial value of the reduction and the storage for the reduced result
558     @param bop binary operator that will be applied in unspecified order to the results of @c uop
559     @param uop unary operator that will be applied to transform each element in the range to the result type
560     @param chunk_size chunk size
561 
562     @return a Task handle
563 
564     The task spawns a subflow to perform parallel reduction over @c init and the transformed elements in the range <tt>[first, last)</tt>. The reduced result is store in @c init. The runtime partitions the range into chunks of size @c chunk_size, where each chunk is processed by a worker.
565 
566     Arguments are templated to enable stateful passing using std::reference_wrapper.
567     */
568     template <typename B, typename E, typename T, typename BOP, typename UOP, typename H = size_t>
569     Task transform_reduce_dynamic(
570       B&& first, E&& last, T& init, BOP&& bop, UOP&& uop, H&& chunk_size = 1
571     );
572 
573 
574   protected:
575 
576     /**
577     @brief constructs a flow builder with a graph
578     */
579     FlowBuilder(Graph& graph);
580 
581     /**
582     @brief associated graph object
583     */
584     Graph& _graph;
585 
586   private:
587 
588     template <typename L>
589     void _linearize(L&);
590 };
591 
592 // Constructor
FlowBuilder(Graph & graph)593 inline FlowBuilder::FlowBuilder(Graph& graph) :
594   _graph {graph} {
595 }
596 
597 // Function: emplace
598 template <typename... C, std::enable_if_t<(sizeof...(C)>1), void>*>
emplace(C &&...cs)599 auto FlowBuilder::emplace(C&&... cs) {
600   return std::make_tuple(emplace(std::forward<C>(cs))...);
601 }
602 
603 // Function: emplace
604 // emplaces a static task
605 template <typename C>
emplace(C && c)606 std::enable_if_t<is_static_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
607   auto n = _graph.emplace_back(
608     nstd::in_place_type_t<Node::StaticWork>{}, std::forward<C>(c)
609   );
610   return Task(n);
611 }
612 
613 // Function: emplace
614 // emplaces a dynamic task
615 template <typename C>
emplace(C && c)616 std::enable_if_t<is_dynamic_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
617   auto n = _graph.emplace_back(
618     nstd::in_place_type_t<Node::DynamicWork>{}, std::forward<C>(c)
619   );
620   return Task(n);
621 }
622 
623 // Function: emplace
624 // emplaces a condition task
625 template <typename C>
emplace(C && c)626 std::enable_if_t<is_condition_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
627   auto n = _graph.emplace_back(
628     nstd::in_place_type_t<Node::ConditionWork>{}, std::forward<C>(c)
629   );
630   return Task(n);
631 }
632 
633 #ifdef TF_ENABLE_CUDA
634 // Function: emplace
635 // emplaces a cudaflow task
636 template <typename C>
emplace(C && c)637 std::enable_if_t<is_cudaflow_task_v<C>, Task> FlowBuilder::emplace(C&& c) {
638   auto n = _graph.emplace_back(
639     nstd::in_place_type_t<Node::cudaFlowWork>{}, std::forward<C>(c)
640   );
641   return Task(n);
642 }
643 #endif
644 
645 // Function: composed_of
composed_of(Taskflow & taskflow)646 inline Task FlowBuilder::composed_of(Taskflow& taskflow) {
647   auto node = _graph.emplace_back(
648     nstd::in_place_type_t<Node::ModuleWork>{}, &taskflow
649   );
650   return Task(node);
651 }
652 
653 // Function: placeholder
placeholder()654 inline Task FlowBuilder::placeholder() {
655   auto node = _graph.emplace_back();
656   return Task(node);
657 }
658 
659 // Procedure: _linearize
660 template <typename L>
_linearize(L & keys)661 void FlowBuilder::_linearize(L& keys) {
662 
663   auto itr = keys.begin();
664   auto end = keys.end();
665 
666   if(itr == end) {
667     return;
668   }
669 
670   auto nxt = itr;
671 
672   for(++nxt; nxt != end; ++nxt, ++itr) {
673     itr->_node->_precede(nxt->_node);
674   }
675 }
676 
677 // Procedure: linearize
linearize(std::vector<Task> & keys)678 inline void FlowBuilder::linearize(std::vector<Task>& keys) {
679   _linearize(keys);
680 }
681 
682 // Procedure: linearize
linearize(std::initializer_list<Task> keys)683 inline void FlowBuilder::linearize(std::initializer_list<Task> keys) {
684   _linearize(keys);
685 }
686 
687 // ----------------------------------------------------------------------------
688 
689 /**
690 @class Subflow
691 
692 @brief building methods of a subflow graph in dynamic tasking
693 
694 By default, a subflow automatically joins its parent node. You may explicitly
695 join or detach a subflow by calling Subflow::join or Subflow::detach.
696 
697 */
698 class Subflow : public FlowBuilder {
699 
700   friend class Executor;
701   friend class FlowBuilder;
702 
703   public:
704 
705     /**
706     @brief enables the subflow to join its parent task
707 
708     Performs an immediate action to join the subflow. Once the subflow is joined,
709     it is considered finished and you may not modify the subflow anymore.
710     */
711     void join();
712 
713     /**
714     @brief enables the subflow to detach from its parent task
715 
716     Performs an immediate action to detach the subflow. Once the subflow is detached,
717     it is considered finished and you may not modify the subflow anymore.
718     */
719     void detach();
720 
721     /**
722     @brief queries if the subflow is joinable
723 
724     When a subflow is joined or detached, it becomes not joinable.
725     */
726     bool joinable() const;
727 
728   private:
729 
730     Subflow(Executor&, Node*, Graph&);
731 
732     Executor& _executor;
733     Node* _parent;
734 
735     bool _joinable {true};
736 };
737 
738 // Constructor
Subflow(Executor & executor,Node * parent,Graph & graph)739 inline Subflow::Subflow(Executor& executor, Node* parent, Graph& graph) :
740   FlowBuilder {graph},
741   _executor   {executor},
742   _parent     {parent} {
743 }
744 
745 // Function: joined
joinable() const746 inline bool Subflow::joinable() const {
747   return _joinable;
748 }
749 
750 // ----------------------------------------------------------------------------
751 // Legacy code
752 // ----------------------------------------------------------------------------
753 
754 using SubflowBuilder = Subflow;
755 
756 }  // end of namespace tf. ---------------------------------------------------
757 
758 
759