| Taskflow: A General-purpose Task-parallel Programming System

Loading...
Searching...
No Matches
graph.hpp
1#pragma once
2
3#include "../utility/macros.hpp"
4#include "../utility/traits.hpp"
5#include "../utility/iterator.hpp"
6
7#ifdef TF_ENABLE_TASK_POOL
8#include "../utility/object_pool.hpp"
9#endif
10
11#include "../utility/os.hpp"
12#include "../utility/math.hpp"
13#include "../utility/small_vector.hpp"
14#include "../utility/serializer.hpp"
15#include "../utility/lazy_string.hpp"
16#include "error.hpp"
17#include "declarations.hpp"
18#include "environment.hpp"
19#include "semaphore.hpp"
20#include "topology.hpp"
21#include "wsq.hpp"
22
23
28
29 namespace tf {
30
31// ----------------------------------------------------------------------------
32// Class: Graph
33// ----------------------------------------------------------------------------
34
47 class Graph {
48
49 friend class Node;
50 friend class FlowBuilder;
51 friend class Subflow;
52 friend class Taskflow;
53 friend class Executor;
54
55 public:
56
60 Graph() = default;
61
65 ~Graph();
66
70 Graph(const Graph&) = delete;
71
75 Graph(Graph&&);
76
80 Graph& operator = (const Graph&) = delete;
81
85 Graph& operator = (Graph&&);
86
90 void clear();
91
95 size_t size() const;
96
100 bool empty() const;
101
105 auto begin();
106
110 auto end();
111
115 auto begin() const;
116
120 auto end() const;
121
122 private:
123
124 std::vector<Node*> _nodes;
125
126 void _erase(Node*);
127
131 template <typename ...ArgsT>
132 Node* _emplace_back(ArgsT&&...);
133};
134
135// ----------------------------------------------------------------------------
136// TaskPriority
137// ----------------------------------------------------------------------------
138
152 enum class TaskPriority : unsigned {
154 HIGH = 0,
156 NORMAL = 1,
158 LOW = 2,
159};
160
161// ----------------------------------------------------------------------------
162// TaskParams
163// ----------------------------------------------------------------------------
164
187 template <typename T>
188 concept StringLike = std::convertible_to<T, std::string_view>;
189
197 class TaskParams {
198
199 public:
200
204 std::string name;
205
209 void* data {nullptr};
210};
211
217 class DefaultTaskParams {};
218
227 template <typename P>
228 concept TaskParamsLike =
229 std::same_as<std::decay_t<P>, TaskParams> ||
230 std::same_as<std::decay_t<P>, DefaultTaskParams> ||
231 StringLike<P>;
232
240 template <typename P>
241 constexpr bool is_task_params_v = TaskParamsLike<P>;
242
243// ----------------------------------------------------------------------------
244// NodeBase
245// ----------------------------------------------------------------------------
246
250 class NodeBase {
251
252 friend class Node;
253 friend class Graph;
254 friend class Task;
255 friend class AsyncTask;
256 friend class TaskView;
257 friend class Taskflow;
258 friend class Executor;
259 friend class FlowBuilder;
260 friend class Subflow;
261 friend class Runtime;
262 friend class NonpreemptiveRuntime;
263 friend class ExplicitAnchorGuard;
264 friend class TaskGroup;
265 friend class Algorithm;
266
267 public:
268
269#ifdef TF_ENABLE_TASK_PRIORITY
270 size_t priority() const {
271 return static_cast<size_t>((_nstate & NSTATE::PRIORITY_MASK) >> NSTATE::PRIORITY_SHIFT);
272 }
273#endif
274
275 protected:
276
277 nstate_t _nstate {NSTATE::NONE};
278 std::atomic<estate_t> _estate {ESTATE::NONE};
279
280 NodeBase* _parent {nullptr};
281 std::atomic<size_t> _join_counter {0};
282
283 std::exception_ptr _exception_ptr {nullptr};
284
285 NodeBase() = default;
286
287 NodeBase(nstate_t nstate, estate_t estate, NodeBase* parent, size_t join_counter) :
288 _nstate {nstate},
289 _estate {estate},
290 _parent {parent},
291 _join_counter {join_counter} {
292 }
293
294#ifdef TF_ENABLE_TASK_PRIORITY
295 void _set_priority([[maybe_unused]] TaskPriority p) {
296 nstate_t encoded = (static_cast<nstate_t>(p)) << NSTATE::PRIORITY_SHIFT;
297 _nstate = (_nstate & ~NSTATE::PRIORITY_MASK) | encoded;
298 }
299#endif
300
301 void _rethrow_exception() {
302 if(_exception_ptr) {
303 auto e = _exception_ptr;
304 _exception_ptr = nullptr;
305 _estate.fetch_and(~(ESTATE::EXCEPTION | ESTATE::CAUGHT), std::memory_order_relaxed);
306 std::rethrow_exception(e);
307 }
308 }
309};
310
311// ----------------------------------------------------------------------------
312// Topology
313// ----------------------------------------------------------------------------
314
318 class Topology : public NodeBase {
319
320 friend class Executor;
321 friend class Subflow;
322 friend class Runtime;
323 friend class NonpreemptiveRuntime;
324 friend class Node;
325
326 template <typename T>
327 friend class Future;
328
329 public:
330
331 template <typename Predicate, typename OnFinish>
332 Topology(Taskflow&, Predicate&&, OnFinish&&);
333
334 bool cancelled() const;
335
336 private:
337
338 Taskflow& _taskflow;
339
340 std::promise<void> _promise;
341
342 std::function<bool()> _predicate;
343 std::function<void()> _on_finish;
344
345 void _carry_out_promise();
346};
347
348// Constructor
349 template <typename Predicate, typename OnFinish>
350Topology::Topology(Taskflow& tf, Predicate&& predicate, OnFinish&& on_finish):
351 NodeBase(NSTATE::NONE, ESTATE::EXPLICITLY_ANCHORED, nullptr, 0),
352 _taskflow(tf),
353 _predicate(std::forward<Predicate>(predicate)),
354 _on_finish(std::forward<OnFinish> (on_finish)) {
355}
356
357// Procedure
358 inline void Topology::_carry_out_promise() {
359 if(_exception_ptr) {
360 auto e = _exception_ptr;
361 _exception_ptr = nullptr;
362 _promise.set_exception(e);
363 }
364 else {
365 _promise.set_value();
366 }
367}
368
369// Function: cancelled
370 inline bool Topology::cancelled() const {
371 return _estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION);
372}
373
374
375// ----------------------------------------------------------------------------
376// Node
377// ----------------------------------------------------------------------------
378
382 class Node : public NodeBase {
383
384 friend class Graph;
385 friend class Task;
386 friend class AsyncTask;
387 friend class TaskView;
388 friend class Taskflow;
389 friend class Executor;
390 friend class FlowBuilder;
391 friend class Subflow;
392 friend class Runtime;
393 friend class NonpreemptiveRuntime;
394 friend class ExplicitAnchorGuard;
395 friend class TaskGroup;
396 friend class Algorithm;
397
398 using Placeholder = std::monostate;
399
400 // static work handle
401 struct Static {
402
403 template <typename C>
404 Static(C&&);
405
406 std::function<void()> work;
407 };
408
409 // runtime work handle
410 struct Runtime {
411
412 template <typename C>
413 Runtime(C&&);
414
415 std::function<void(tf::Runtime&)> work;
416 };
417
418 struct NonpreemptiveRuntime {
419
420 template <typename C>
421 NonpreemptiveRuntime(C&&);
422
423 std::function<void(tf::NonpreemptiveRuntime&)> work;
424 };
425
426 // subflow work handle
427 struct Subflow {
428
429 template <typename C>
430 Subflow(C&&);
431
432 std::function<void(tf::Subflow&)> work;
433 Graph subgraph;
434 };
435
436 // condition work handle
437 struct Condition {
438
439 template <typename C>
440 Condition(C&&);
441
442 std::function<int()> work;
443 };
444
445 // multi-condition work handle
446 struct MultiCondition {
447
448 template <typename C>
449 MultiCondition(C&&);
450
451 std::function<SmallVector<int>()> work;
452 };
453
454 // module work handle
455 struct Module {
456
457 Module(Graph&);
458
459 Graph& graph;
460 };
461
462 // adopted module work handle
463 struct AdoptedModule {
464
465 AdoptedModule(Graph&&);
466
467 Graph graph;
468 };
469
470 // Async work
471 struct Async {
472
473 template <typename T>
474 Async(T&&);
475
476 std::variant<
477 std::function<void()>,
478 std::function<void(tf::Runtime&)>, // silent async
479 std::function<void(tf::Runtime&, bool)> // async
480 > work;
481 };
482
483 // silent dependent async
484 struct DependentAsync {
485
486 template <typename C>
487 DependentAsync(C&&);
488
489 std::variant<
490 std::function<void()>,
491 std::function<void(tf::Runtime&)>, // silent async
492 std::function<void(tf::Runtime&, bool)> // async
493 > work;
494
495 // use_count is packed into the lower 24 bits of NodeBase::_estate
496 // (ESTATE::REFCOUNT_MASK) to avoid a separate atomic and a std::get_if
497 // call on every AsyncTask copy/move/destroy. see ESTATE::REFCOUNT_ONE.
498 };
499
500 using handle_t = std::variant<
501 Placeholder, // placeholder
502 Static, // static tasking
503 Runtime, // runtime tasking
504 NonpreemptiveRuntime, // runtime (non-preemptive) tasking
505 Subflow, // subflow tasking
506 Condition, // conditional tasking
507 MultiCondition, // multi-conditional tasking
508 Module, // composable tasking
509 AdoptedModule, // composable tasking with move semantics
510 Async, // async tasking
511 DependentAsync // dependent async tasking
512 >;
513
514 struct Semaphores {
515 SmallVector<Semaphore*> to_acquire;
516 SmallVector<Semaphore*> to_release;
517 };
518
519 public:
520
521 // variant index
522 constexpr static auto PLACEHOLDER = get_index_v<Placeholder, handle_t>;
523 constexpr static auto STATIC = get_index_v<Static, handle_t>;
524 constexpr static auto RUNTIME = get_index_v<Runtime, handle_t>;
525 constexpr static auto NONPREEMPTIVE_RUNTIME = get_index_v<NonpreemptiveRuntime, handle_t>;
526 constexpr static auto SUBFLOW = get_index_v<Subflow, handle_t>;
527 constexpr static auto CONDITION = get_index_v<Condition, handle_t>;
528 constexpr static auto MULTI_CONDITION = get_index_v<MultiCondition, handle_t>;
529 constexpr static auto MODULE = get_index_v<Module, handle_t>;
530 constexpr static auto ADOPTED_MODULE = get_index_v<AdoptedModule, handle_t>;
531 constexpr static auto ASYNC = get_index_v<Async, handle_t>;
532 constexpr static auto DEPENDENT_ASYNC = get_index_v<DependentAsync, handle_t>;
533
534 Node() = default;
535
536 template <typename... Args>
537 Node(nstate_t, estate_t, const TaskParams&, Topology*, NodeBase*, size_t, Args&&...);
538
539 template <typename... Args>
540 Node(nstate_t, estate_t, const DefaultTaskParams&, Topology*, NodeBase*, size_t, Args&&...);
541
542 template <StringLike S, typename... Args>
543 Node(nstate_t, estate_t, S&&, Topology*, NodeBase*, size_t, Args&&...);
544
545 size_t num_successors() const;
546 size_t num_predecessors() const;
547 size_t num_strong_dependencies() const;
548 size_t num_weak_dependencies() const;
549
550 const std::string& name() const;
551
552 private:
553
554 std::string _name;
555
556 void* _data {nullptr};
557
558 Topology* _topology {nullptr};
559
560 size_t _num_successors {0};
561 SmallVector<Node*, 4> _edges;
562
563 handle_t _handle;
564
565 std::unique_ptr<Semaphores> _semaphores;
566
567 bool _is_parent_cancelled() const;
568 bool _is_conditioner() const;
569 bool _acquire_all(SmallVector<Node*>&);
570 void _release_all(SmallVector<Node*>&);
571 void _precede(Node*);
572 void _set_up_join_counter();
573
574 void _remove_successors(Node*);
575 void _remove_predecessors(Node*);
576};
577
578
579// ----------------------------------------------------------------------------
580// Definition for Node::Static
581// ----------------------------------------------------------------------------
582
583// Constructor
584 template <typename C>
585Node::Static::Static(C&& c) : work {std::forward<C>(c)} {
586}
587
588// ----------------------------------------------------------------------------
589// Definition for Node::Runtime
590// ----------------------------------------------------------------------------
591
592// Constructor
593 template <typename C>
594Node::Runtime::Runtime(C&& c) : work {std::forward<C>(c)} {
595}
596
597// Constructor
598 template <typename C>
599Node::NonpreemptiveRuntime::NonpreemptiveRuntime(C&& c) : work {std::forward<C>(c)} {
600}
601
602// ----------------------------------------------------------------------------
603// Definition for Node::Subflow
604// ----------------------------------------------------------------------------
605
606// Constructor
607 template <typename C>
608Node::Subflow::Subflow(C&& c) : work {std::forward<C>(c)} {
609}
610
611// ----------------------------------------------------------------------------
612// Definition for Node::Condition
613// ----------------------------------------------------------------------------
614
615// Constructor
616 template <typename C>
617Node::Condition::Condition(C&& c) : work {std::forward<C>(c)} {
618}
619
620// ----------------------------------------------------------------------------
621// Definition for Node::MultiCondition
622// ----------------------------------------------------------------------------
623
624// Constructor
625 template <typename C>
626Node::MultiCondition::MultiCondition(C&& c) : work {std::forward<C>(c)} {
627}
628
629// ----------------------------------------------------------------------------
630// Definition for Node::Module
631// ----------------------------------------------------------------------------
632
633// Constructor
634 inline Node::Module::Module(Graph& g) : graph(g){
635}
636
637// Constructor
638 inline Node::AdoptedModule::AdoptedModule(Graph&& g) : graph(std::move(g)){
639}
640
641// ----------------------------------------------------------------------------
642// Definition for Node::Async
643// ----------------------------------------------------------------------------
644
645// Constructor
646 template <typename C>
647Node::Async::Async(C&& c) : work {std::forward<C>(c)} {
648}
649
650// ----------------------------------------------------------------------------
651// Definition for Node::DependentAsync
652// ----------------------------------------------------------------------------
653
654// Constructor
655 template <typename C>
656Node::DependentAsync::DependentAsync(C&& c) : work {std::forward<C>(c)} {
657}
658
659// ----------------------------------------------------------------------------
660// Definition for Node
661// ----------------------------------------------------------------------------
662
663// Constructor
664 template <typename... Args>
665Node::Node(
666 nstate_t nstate,
667 estate_t estate,
668 const TaskParams& params,
669 Topology* topology,
670 NodeBase* parent,
671 size_t join_counter,
672 Args&&... args
673) :
674 NodeBase(nstate, estate, parent, join_counter),
675 _name {params.name},
676 _data {params.data},
677 _topology {topology},
678 _handle {std::forward<Args>(args)...} {
679}
680
681// Constructor
682 template <typename... Args>
683Node::Node(
684 nstate_t nstate,
685 estate_t estate,
686 const DefaultTaskParams&,
687 Topology* topology,
688 NodeBase* parent,
689 size_t join_counter,
690 Args&&... args
691) :
692 NodeBase(nstate, estate, parent, join_counter),
693 _topology {topology},
694 _handle {std::forward<Args>(args)...} {
695}
696
697// Constructor
698 template <StringLike S, typename... Args>
699Node::Node(
700 nstate_t nstate,
701 estate_t estate,
702 S&& name,
703 Topology* topology,
704 NodeBase* parent,
705 size_t join_counter,
706 Args&&... args
707) :
708 NodeBase(nstate, estate, parent, join_counter),
709 _name {std::forward<S>(name)},
710 _topology {topology},
711 _handle {std::forward<Args>(args)...} {
712}
713
715//template <typename T, typename... Args>
716//void Node::reset(
717// nstate_t nstate,
718// estate_t estate,
719// const TaskParams& params,
720// Topology* topology,
721// NodeBase* parent,
722// size_t join_counter,
723// std::in_place_type_t<T>,
724// Args&&... args
725//) {
726// _nstate = nstate;
727// _estate = estate;
728// _parent = parent;
729// _join_counter.store(join_counter, std::memory_order_relaxed);
730// _exception_ptr = nullptr;
731// _name = params.name;
732// _data = params.data;
733// _topology = topology;
734// _handle.emplace<T>(std::forward<Args>(args)...);
735// _num_successors = 0;
736// _edges.clear();
737// _semaphores.reset();
738//}
739//
741//template <typename T, typename... Args>
742//void Node::reset(
743// nstate_t nstate,
744// estate_t estate,
745// const DefaultTaskParams&,
746// Topology* topology,
747// NodeBase* parent,
748// size_t join_counter,
749// std::in_place_type_t<T>,
750// Args&&... args
751//) {
752// _nstate = nstate;
753// _estate = estate;
754// _parent = parent;
755// _join_counter.store(join_counter, std::memory_order_relaxed);
756// _exception_ptr = nullptr;
757// _name.clear();
758// _data = nullptr;
759// _topology = topology;
760// _handle.emplace<T>(std::forward<Args>(args)...);
761// _num_successors = 0;
762// _edges.clear();
763// _semaphores.reset();
764//}
765
766// Procedure: _precede
767/*
768 u edges layout: s1, s2, s3, p1, p2 (num_successors = 3)
769 v edges layout: s1, p1, p2
770
771 add a new successor: u->v
772 u successor layout:
773 s1, s2, s3, p1, p2, v (push_back v)
774 s1, s2, s3, v, p2, p1 (swap edges[num_successors] with edges[n-1])
775 v predecessor layout:
776 s1, p1, p2, u (push_back u)
777*/
778 inline void Node::_precede(Node* v) {
779 _edges.push_back(v);
780 std::swap(_edges[_num_successors++], _edges[_edges.size() - 1]);
781 v->_edges.push_back(this);
782}
783
784// Function: _remove_successors
785 inline void Node::_remove_successors(Node* node) {
786 auto sit = std::remove(_edges.begin(), _edges.begin() + _num_successors, node);
787 size_t new_num_successors = std::distance(_edges.begin(), sit);
788 std::move(_edges.begin() + _num_successors, _edges.end(), sit);
789 _edges.resize(_edges.size() - (_num_successors - new_num_successors));
790 _num_successors = new_num_successors;
791}
792
793// Function: _remove_predecessors
794 inline void Node::_remove_predecessors(Node* node) {
795 _edges.erase(
796 std::remove(_edges.begin() + _num_successors, _edges.end(), node), _edges.end()
797 );
798}
799
800// Function: num_successors
801 inline size_t Node::num_successors() const {
802 return _num_successors;
803}
804
805// Function: predecessors
806 inline size_t Node::num_predecessors() const {
807 return _edges.size() - _num_successors;
808}
809
810// Function: num_weak_dependencies
811 inline size_t Node::num_weak_dependencies() const {
812 size_t n = 0;
813 for(size_t i=_num_successors; i<_edges.size(); i++) {
814 n += _edges[i]->_is_conditioner();
815 }
816 return n;
817}
818
819// Function: num_strong_dependencies
820 inline size_t Node::num_strong_dependencies() const {
821 size_t n = 0;
822 for(size_t i=_num_successors; i<_edges.size(); i++) {
823 n += !_edges[i]->_is_conditioner();
824 }
825 return n;
826}
827
828// Function: name
829 inline const std::string& Node::name() const {
830 return _name;
831}
832
833// Function: _is_conditioner
834 inline bool Node::_is_conditioner() const {
835 return _handle.index() == Node::CONDITION ||
836 _handle.index() == Node::MULTI_CONDITION;
837}
838
839// Function: _is_parent_cancelled
840 inline bool Node::_is_parent_cancelled() const {
841 return (_topology && (_topology->_estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION)))
842 ||
843 (_parent && (_parent->_estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION)));
844}
845
846// Procedure: _set_up_join_counter
847 inline void Node::_set_up_join_counter() {
848 //assert(_nstate == NSTATE::NONE);
849 for(size_t i=_num_successors; i<_edges.size(); i++) {
850 _nstate += !_edges[i]->_is_conditioner();
851 }
852 _join_counter.store(_nstate & NSTATE::STRONG_DEPENDENCIES_MASK, std::memory_order_relaxed);
853}
854
855
856// Function: _acquire_all
857 inline bool Node::_acquire_all(SmallVector<Node*>& nodes) {
858 // assert(_semaphores != nullptr);
859 auto& to_acquire = _semaphores->to_acquire;
860 for(size_t i = 0; i < to_acquire.size(); ++i) {
861 if(!to_acquire[i]->_try_acquire_or_wait(this)) {
862 for(size_t j = 1; j <= i; ++j) {
863 to_acquire[i-j]->_release(nodes);
864 }
865 return false;
866 }
867 }
868 return true;
869}
870
871// Function: _release_all
872 inline void Node::_release_all(SmallVector<Node*>& nodes) {
873 // assert(_semaphores != nullptr);
874 auto& to_release = _semaphores->to_release;
875 for(const auto& sem : to_release) {
876 sem->_release(nodes);
877 }
878}
879
880
881
882// ----------------------------------------------------------------------------
883// ExplicitAnchorGuard
884// ----------------------------------------------------------------------------
885
889 class ExplicitAnchorGuard {
890
891 public:
892
893 // Explicit anchor must sit in estate as it may be accessed by multiple threads
894 // (e.g., corun's parent with tear_down_async's parent).
895 ExplicitAnchorGuard(NodeBase* node_base) : _node_base{node_base} {
896 _node_base->_estate.fetch_or(ESTATE::EXPLICITLY_ANCHORED, std::memory_order_relaxed);
897 }
898
899 ~ExplicitAnchorGuard() {
900 _node_base->_estate.fetch_and(~ESTATE::EXPLICITLY_ANCHORED, std::memory_order_relaxed);
901 }
902
903 private:
904
905 NodeBase* _node_base;
906};
907
908// ----------------------------------------------------------------------------
909// Node Object Pool
910// ----------------------------------------------------------------------------
911
912#ifdef TF_ENABLE_TASK_POOL
916 using NodePool = std::conditional_t<
917 std::atomic<tf::TaggedHead128>::is_always_lock_free,
918 ObjectPool<Node, tf::TaggedHead128>,
919 ObjectPool<Node, tf::TaggedHead64<>>
920>;
921 inline NodePool _node_pool;
922#endif
923
927 template <typename... ArgsT>
928TF_FORCE_INLINE Node* animate(ArgsT&&... args) {
929#ifdef TF_ENABLE_TASK_POOL
930 return _node_pool.animate(std::forward<ArgsT>(args)...);
931#else
932 return new Node(std::forward<ArgsT>(args)...);
933#endif
934}
935
939TF_FORCE_INLINE void recycle(Node* ptr) {
940#ifdef TF_ENABLE_TASK_POOL
941 _node_pool.recycle(ptr);
942#else
943 delete ptr;
944#endif
945}
946
947
948// ----------------------------------------------------------------------------
949// Graph definition
950// ----------------------------------------------------------------------------
951
952// Destructor
953 inline Graph::~Graph() {
954 clear();
955}
956
957// Move constructor
958 inline Graph::Graph(Graph&& other) :
959 _nodes {std::move(other._nodes)} {
960}
961
962// Move assignment
963 inline Graph& Graph::operator = (Graph&& other) {
964 clear();
965 _nodes = std::move(other._nodes);
966 return *this;
967}
968
969// Procedure: clear
970 inline void Graph::clear() {
971 for(auto node : _nodes) {
972 recycle(node);
973 }
974 _nodes.clear();
975}
976
977// Function: size
978 inline size_t Graph::size() const {
979 return _nodes.size();
980}
981
982// Function: empty
983 inline bool Graph::empty() const {
984 return _nodes.empty();
985}
986
987// Function: begin
988 inline auto Graph::begin() {
989 return _nodes.begin();
990}
991
992// Function: end
993 inline auto Graph::end() {
994 return _nodes.end();
995}
996
997// Function: begin
998 inline auto Graph::begin() const {
999 return _nodes.begin();
1000}
1001
1002// Function: end
1003 inline auto Graph::end() const {
1004 return _nodes.end();
1005}
1006
1007// Function: erase
1008 inline void Graph::_erase(Node* node) {
1009 //erase(
1010 // std::remove_if(begin(), end(), [&](auto& p){ return p.get() == node; }),
1011 // end()
1012 //);
1013 _nodes.erase(
1014 std::remove_if(_nodes.begin(), _nodes.end(), [&](auto& p){
1015 if(p == node) {
1016 recycle(p);
1017 return true;
1018 }
1019 return false;
1020 }),
1021 _nodes.end()
1022 );
1023}
1024
1028 template <typename ...ArgsT>
1029Node* Graph::_emplace_back(ArgsT&&... args) {
1030 _nodes.push_back(animate(std::forward<ArgsT>(args)...));
1031 return _nodes.back();
1032}
1033
1034// ----------------------------------------------------------------------------
1035// Graph checker
1036// ----------------------------------------------------------------------------
1037
1038
1068 template <typename T>
1069 concept GraphLike = std::derived_from<T, Graph> ||
1070 requires(T& t) {
1071 { t.graph() } -> std::convertible_to<Graph&>;
1072 };
1073
1107 template <GraphLike T>
1108 Graph& retrieve_graph(T& target) {
1109 if constexpr (requires { target.graph(); }) {
1110 return target.graph();
1111 } else {
1112 return static_cast<Graph&>(target);
1113 }
1114}
1115
1116} // end of namespace tf. ----------------------------------------------------
tf::AsyncTask
class to hold a dependent asynchronous task with shared ownership
Definition async_task.hpp:45
tf::DefaultTaskParams
class to create an empty task parameter for compile-time optimization
Definition graph.hpp:217
tf::Executor
class to create an executor
Definition executor.hpp:62
tf::FlowBuilder
class to build a task dependency graph
Definition flow_builder.hpp:114
tf::Graph
class to create a graph object
Definition graph.hpp:47
tf::Graph::operator=
Graph & operator=(const Graph &)=delete
disabled copy assignment operator
tf::Graph::Graph
Graph()=default
constructs the graph object
tf::Graph::empty
bool empty() const
queries the emptiness of the graph
Definition graph.hpp:983
tf::Graph::~Graph
~Graph()
destroys the graph object
Definition graph.hpp:953
tf::Graph::end
auto end()
returns an iterator past the last element of this graph
Definition graph.hpp:993
tf::Graph::size
size_t size() const
returns the number of nodes in the graph
Definition graph.hpp:978
tf::Graph::clear
void clear()
clears the graph
Definition graph.hpp:970
tf::Graph::begin
auto begin()
returns an iterator to the first node of this graph
Definition graph.hpp:988
tf::Graph::Graph
Graph(const Graph &)=delete
disabled copy constructor
tf::Runtime
class to create a runtime task
Definition runtime.hpp:47
tf::Subflow
class to construct a subflow graph from the execution of a dynamic task
Definition flow_builder.hpp:1913
tf::TaskGroup
class to create a task group from a task
Definition task_group.hpp:61
tf::TaskParams
class to create a task parameter object
Definition graph.hpp:197
tf::TaskParams::name
std::string name
name of the task
Definition graph.hpp:204
tf::TaskParams::data
void * data
C-styled pointer to user data.
Definition graph.hpp:209
tf::TaskView
class to access task information from the observer interface
Definition task.hpp:1613
tf::Task
class to create a task handle over a taskflow node
Definition task.hpp:569
tf::Taskflow
class to create a taskflow object
Definition taskflow.hpp:64
tf::GraphLike
concept to check if a type owns or provides access to a tf::Graph
Definition graph.hpp:1069
tf::StringLike
concept to check if a type is convertible to std::string_view
Definition graph.hpp:188
tf::TaskParamsLike
concept to check if a type is a task parameter
Definition graph.hpp:228
tf
taskflow namespace
Definition small_vector.hpp:20
tf::TaskType::MODULE
@ MODULE
module task type
Definition task.hpp:33
tf::TaskType::SUBFLOW
@ SUBFLOW
dynamic (subflow) task type
Definition task.hpp:29
tf::TaskType::CONDITION
@ CONDITION
condition task type
Definition task.hpp:31
tf::TaskType::ASYNC
@ ASYNC
asynchronous task type
Definition task.hpp:35
tf::TaskType::PLACEHOLDER
@ PLACEHOLDER
placeholder task type
Definition task.hpp:23
tf::TaskType::RUNTIME
@ RUNTIME
runtime task type
Definition task.hpp:27
tf::TaskType::STATIC
@ STATIC
static task type
Definition task.hpp:25
tf::retrieve_graph
Graph & retrieve_graph(T &target)
retrieves a reference to the underlying tf::Graph from an object
Definition graph.hpp:1108
tf::TaskPriority
TaskPriority
enumeration of all task priority levels
Definition graph.hpp:152
tf::TaskPriority::NORMAL
@ NORMAL
the normal task priority level
Definition graph.hpp:156
tf::TaskPriority::LOW
@ LOW
the lowest task priority level (least urgent)
Definition graph.hpp:158
tf::TaskPriority::HIGH
@ HIGH
the highest task priority level (most urgent)
Definition graph.hpp:154
tf::is_task_params_v
constexpr bool is_task_params_v
concept that determines if a type is a task parameter type (variable template)
Definition graph.hpp:241