Loading...
Searching...
No Matches
task.hpp
1#pragma once
2
3#include "graph.hpp"
4
9
10namespace tf {
11
12// ----------------------------------------------------------------------------
13// Task Types
14// ----------------------------------------------------------------------------
15
39
44inline constexpr std::array<TaskType, 7> TASK_TYPES = {
52};
53
66inline const char* to_string(TaskType type) {
67
68 const char* val;
69
70 switch(type) {
71 case TaskType::PLACEHOLDER: val = "placeholder"; break;
72 case TaskType::STATIC: val = "static"; break;
73 case TaskType::RUNTIME: val = "runtime"; break;
74 case TaskType::SUBFLOW: val = "subflow"; break;
75 case TaskType::CONDITION: val = "condition"; break;
76 case TaskType::MODULE: val = "module"; break;
77 case TaskType::ASYNC: val = "async"; break;
78 default: val = "undefined"; break;
79 }
80
81 return val;
82}
83
84// ----------------------------------------------------------------------------
85// Static Task Trait
86// ----------------------------------------------------------------------------
87
93template <typename C>
94concept StaticTaskLike = std::invocable<C> &&
95 std::same_as<std::invoke_result_t<C>, void>;
96
104template <typename C>
106
107// ----------------------------------------------------------------------------
108// Subflow Task Trait
109// ----------------------------------------------------------------------------
110
116template <typename C>
117concept SubflowTaskLike = std::invocable<C, tf::Subflow&> &&
118 std::same_as<std::invoke_result_t<C, tf::Subflow&>, void>;
119
127template <typename C>
129
130// ----------------------------------------------------------------------------
131// Runtime Task Trait
132// ----------------------------------------------------------------------------
133
140template <typename C>
142 (std::invocable<C, tf::Runtime&> &&
143 std::same_as<std::invoke_result_t<C, tf::Runtime&>, void>) ||
144 (std::invocable<C, tf::NonpreemptiveRuntime&> &&
145 std::same_as<std::invoke_result_t<C, tf::NonpreemptiveRuntime&>, void>);
146
154template <typename C>
156
157
158// ----------------------------------------------------------------------------
159// Condition Task Trait
160// ----------------------------------------------------------------------------
161
167template <typename C>
168concept ConditionTaskLike = std::invocable<C> &&
169 std::convertible_to<std::invoke_result_t<C>, int>;
170
178template <typename C>
180
187template <typename C>
188concept MultiConditionTaskLike = std::invocable<C> &&
189 std::same_as<std::invoke_result_t<C>, SmallVector<int>>;
190
198template <typename C>
200
201
202// ----------------------------------------------------------------------------
203// Task
204// ----------------------------------------------------------------------------
205
263class Task {
264
265 friend class FlowBuilder;
266 friend class Runtime;
267 friend class NonpreemptiveRuntime;
268 friend class Taskflow;
269 friend class TaskView;
270 friend class Executor;
271
272 public:
273
279 Task() = default;
280
293 Task(const Task& other);
294
306 Task& operator = (const Task& other);
307
316 Task& operator = (std::nullptr_t);
317
330 bool operator == (const Task& rhs) const;
331
344 bool operator != (const Task& rhs) const;
345
357 const std::string& name() const;
358
371 size_t num_successors() const;
372
385 size_t num_predecessors() const;
386
414 size_t num_strong_dependencies() const;
415
443 size_t num_weak_dependencies() const;
444
457 Task& name(const std::string& name);
458
481 template <typename C>
482 Task& work(C&& callable);
483
501 template <GraphLike T>
502 Task& composed_of(T& object);
503
525 Task& adopt(tf::Graph&& graph);
526
546 template <typename... Ts>
547 Task& precede(Ts&&... tasks);
548
568 template <typename... Ts>
569 Task& succeed(Ts&&... tasks);
570
598 template <typename... Ts>
599 Task& remove_predecessors(Ts&&... tasks);
600
628 template <typename... Ts>
629 Task& remove_successors(Ts&&... tasks);
630
637 Task& release(Semaphore& semaphore);
638
645 template <typename I>
646 Task& release(I first, I last);
647
654 Task& acquire(Semaphore& semaphore);
655
662 template <typename I>
663 Task& acquire(I first, I last);
664
694 Task& data(void* data);
695
708 void reset();
709
713 void reset_work();
714
729 bool empty() const;
730
744 bool has_work() const;
745
768 template <typename V>
769 void for_each_successor(V&& visitor) const;
770
792 template <typename V>
793 void for_each_predecessor(V&& visitor) const;
794
815 template <typename V>
816 void for_each_subflow_task(V&& visitor) const;
817
830 size_t hash_value() const;
831
844 TaskType type() const;
845
855 void dump(std::ostream& ostream) const;
856
884 void* data() const;
885
926 std::exception_ptr exception_ptr() const;
927
933 bool has_exception_ptr() const;
934
935 private:
936
937 Task(Node*);
938
939 Node* _node {nullptr};
940};
941
942// Constructor
943inline Task::Task(Node* node) : _node {node} {
944}
945
946// Constructor
947inline Task::Task(const Task& rhs) : _node {rhs._node} {
948}
949
950// Function: precede
951template <typename... Ts>
952Task& Task::precede(Ts&&... tasks) {
953 (_node->_precede(tasks._node), ...);
954 //_precede(std::forward<Ts>(tasks)...);
955 return *this;
956}
957
958// Function: succeed
959template <typename... Ts>
960Task& Task::succeed(Ts&&... tasks) {
961 (tasks._node->_precede(_node), ...);
962 //_succeed(std::forward<Ts>(tasks)...);
963 return *this;
964}
965
966// Function: remove_predecessors
967template <typename... Ts>
969 (tasks._node->_remove_successors(_node), ...);
970 (_node->_remove_predecessors(tasks._node), ...);
971 return *this;
972}
973
974// Function: remove_successors
975template <typename... Ts>
977 (_node->_remove_successors(tasks._node), ...);
978 (tasks._node->_remove_predecessors(_node), ...);
979 return *this;
980}
981
982// Function: composed_of
983template <GraphLike T>
985 _node->_handle.emplace<Node::Module>(retrieve_graph(target));
986 return *this;
987}
988
989// Function: adopt
990inline Task& Task::adopt(Graph&& graph) {
991 _node->_handle.emplace<Node::AdoptedModule>(std::move(graph));
992 return *this;
993}
994
995// Operator =
996inline Task& Task::operator = (const Task& rhs) {
997 _node = rhs._node;
998 return *this;
999}
1000
1001// Operator =
1002inline Task& Task::operator = (std::nullptr_t ptr) {
1003 _node = ptr;
1004 return *this;
1005}
1006
1007// Operator ==
1008inline bool Task::operator == (const Task& rhs) const {
1009 return _node == rhs._node;
1010}
1011
1012// Operator !=
1013inline bool Task::operator != (const Task& rhs) const {
1014 return _node != rhs._node;
1015}
1016
1017// Function: name
1018inline Task& Task::name(const std::string& name) {
1019 _node->_name = name;
1020 return *this;
1021}
1022
1023// Function: acquire
1025 if(!_node->_semaphores) {
1026 _node->_semaphores = std::make_unique<Node::Semaphores>();
1027 }
1028 _node->_semaphores->to_acquire.push_back(&s);
1029 return *this;
1030}
1031
1032// Function: acquire
1033template <typename I>
1034Task& Task::acquire(I first, I last) {
1035 if(!_node->_semaphores) {
1036 _node->_semaphores = std::make_unique<Node::Semaphores>();
1037 }
1038 _node->_semaphores->to_acquire.reserve(
1039 _node->_semaphores->to_acquire.size() + std::distance(first, last)
1040 );
1041 for(auto s = first; s != last; ++s){
1042 _node->_semaphores->to_acquire.push_back(&(*s));
1043 }
1044 return *this;
1045}
1046
1047// Function: release
1049 if(!_node->_semaphores) {
1050 _node->_semaphores = std::make_unique<Node::Semaphores>();
1051 }
1052 _node->_semaphores->to_release.push_back(&s);
1053 return *this;
1054}
1055
1056// Function: release
1057template <typename I>
1058Task& Task::release(I first, I last) {
1059 if(!_node->_semaphores) {
1060 _node->_semaphores = std::make_unique<Node::Semaphores>();
1061 }
1062 _node->_semaphores->to_release.reserve(
1063 _node->_semaphores->to_release.size() + std::distance(first, last)
1064 );
1065 for(auto s = first; s != last; ++s) {
1066 _node->_semaphores->to_release.push_back(&(*s));
1067 }
1068 return *this;
1069}
1070
1071// Procedure: reset
1072inline void Task::reset() {
1073 _node = nullptr;
1074}
1075
1076// Procedure: reset_work
1077inline void Task::reset_work() {
1078 _node->_handle.emplace<std::monostate>();
1079}
1080
1081// Function: name
1082inline const std::string& Task::name() const {
1083 return _node->_name;
1084}
1085
1086// Function: num_predecessors
1087inline size_t Task::num_predecessors() const {
1088 return _node->num_predecessors();
1089}
1090
1091// Function: num_strong_dependencies
1092inline size_t Task::num_strong_dependencies() const {
1093 return _node->num_strong_dependencies();
1094}
1095
1096// Function: num_weak_dependencies
1097inline size_t Task::num_weak_dependencies() const {
1098 return _node->num_weak_dependencies();
1099}
1100
1101// Function: num_successors
1102inline size_t Task::num_successors() const {
1103 return _node->num_successors();
1104}
1105
1106// Function: empty
1107inline bool Task::empty() const {
1108 return _node == nullptr;
1109}
1110
1111// Function: has_work
1112inline bool Task::has_work() const {
1113 return _node ? _node->_handle.index() != 0 : false;
1114}
1115
1116// Function: exception
1117inline std::exception_ptr Task::exception_ptr() const {
1118 return _node ? _node->_exception_ptr : nullptr;
1119}
1120
1121// Function: has_exception
1122inline bool Task::has_exception_ptr() const {
1123 return _node ? (_node->_exception_ptr != nullptr) : false;
1124}
1125
1126// Function: task_type
1127inline TaskType Task::type() const {
1128 switch(_node->_handle.index()) {
1129 case Node::PLACEHOLDER: return TaskType::PLACEHOLDER;
1130 case Node::STATIC: return TaskType::STATIC;
1131 case Node::RUNTIME: return TaskType::RUNTIME;
1132 case Node::NONPREEMPTIVE_RUNTIME: return TaskType::RUNTIME;
1133 case Node::SUBFLOW: return TaskType::SUBFLOW;
1134 case Node::CONDITION: return TaskType::CONDITION;
1135 case Node::MULTI_CONDITION: return TaskType::CONDITION;
1136 case Node::MODULE: return TaskType::MODULE;
1137 case Node::ADOPTED_MODULE: return TaskType::MODULE;
1138 case Node::ASYNC: return TaskType::ASYNC;
1139 case Node::DEPENDENT_ASYNC: return TaskType::ASYNC;
1140 default: return TaskType::UNDEFINED;
1141 }
1142}
1143
1144// Function: for_each_successor
1145template <typename V>
1146void Task::for_each_successor(V&& visitor) const {
1147 for(size_t i=0; i<_node->_num_successors; ++i) {
1148 visitor(Task(_node->_edges[i]));
1149 }
1150}
1151
1152// Function: for_each_predecessor
1153template <typename V>
1154void Task::for_each_predecessor(V&& visitor) const {
1155 for(size_t i=_node->_num_successors; i<_node->_edges.size(); ++i) {
1156 visitor(Task(_node->_edges[i]));
1157 }
1158}
1159
1160// Function: for_each_subflow_task
1161template <typename V>
1162void Task::for_each_subflow_task(V&& visitor) const {
1163 if(auto ptr = std::get_if<Node::Subflow>(&_node->_handle); ptr) {
1164 for(auto itr = ptr->subgraph.begin(); itr != ptr->subgraph.end(); ++itr) {
1165 visitor(Task(*itr));
1166 }
1167 }
1168}
1169
1170// Function: hash_value
1171inline size_t Task::hash_value() const {
1172 return std::hash<Node*>{}(_node);
1173}
1174
1175// Procedure: dump
1176inline void Task::dump(std::ostream& os) const {
1177 os << "task ";
1178 if(name().empty()) os << _node;
1179 else os << name();
1180 os << " [type=" << to_string(type()) << ']';
1181}
1182
1183// Function: work
1184template <typename C>
1186
1187 if constexpr(is_static_task_v<C>) {
1188 _node->_handle.emplace<Node::Static>(std::forward<C>(c));
1189 }
1190 else if constexpr(is_runtime_task_v<C>) {
1191 _node->_handle.emplace<Node::Runtime>(std::forward<C>(c));
1192 }
1193 else if constexpr(is_subflow_task_v<C>) {
1194 _node->_handle.emplace<Node::Subflow>(std::forward<C>(c));
1195 }
1196 else if constexpr(is_condition_task_v<C>) {
1197 _node->_handle.emplace<Node::Condition>(std::forward<C>(c));
1198 }
1199 else if constexpr(is_multi_condition_task_v<C>) {
1200 _node->_handle.emplace<Node::MultiCondition>(std::forward<C>(c));
1201 }
1202 else {
1203 static_assert(dependent_false_v<C>, "invalid task callable");
1204 }
1205 return *this;
1206}
1207
1208// Function: data
1209inline void* Task::data() const {
1210 return _node->_data;
1211}
1212
1213// Function: data
1214inline Task& Task::data(void* data) {
1215 _node->_data = data;
1216 return *this;
1217}
1218
1219// ----------------------------------------------------------------------------
1220// global ostream
1221// ----------------------------------------------------------------------------
1222
1226inline std::ostream& operator << (std::ostream& os, const Task& task) {
1227 task.dump(os);
1228 return os;
1229}
1230
1231// ----------------------------------------------------------------------------
1232// Task View
1233// ----------------------------------------------------------------------------
1234
1240class TaskView {
1241
1242 friend class Executor;
1243
1244 public:
1245
1249 const std::string& name() const;
1250
1254 size_t num_successors() const;
1255
1259 size_t num_predecessors() const;
1260
1264 size_t num_strong_dependencies() const;
1265
1269 size_t num_weak_dependencies() const;
1270
1279 template <typename V>
1280 void for_each_successor(V&& visitor) const;
1281
1290 template <typename V>
1291 void for_each_predecessor(V&& visitor) const;
1292
1296 TaskType type() const;
1297
1301 size_t hash_value() const;
1302
1303 private:
1304
1305 TaskView(const Node&);
1306 TaskView(const TaskView&) = default;
1307
1308 const Node& _node;
1309};
1310
1311// Constructor
1312inline TaskView::TaskView(const Node& node) : _node {node} {
1313}
1314
1315// Function: name
1316inline const std::string& TaskView::name() const {
1317 return _node._name;
1318}
1319
1320// Function: num_predecessors
1321inline size_t TaskView::num_predecessors() const {
1322 return _node.num_predecessors();
1323}
1324
1325// Function: num_strong_dependencies
1327 return _node.num_strong_dependencies();
1328}
1329
1330// Function: num_weak_dependencies
1331inline size_t TaskView::num_weak_dependencies() const {
1332 return _node.num_weak_dependencies();
1333}
1334
1335// Function: num_successors
1336inline size_t TaskView::num_successors() const {
1337 return _node.num_successors();
1338}
1339
1340// Function: type
1341inline TaskType TaskView::type() const {
1342 switch(_node._handle.index()) {
1343 case Node::PLACEHOLDER: return TaskType::PLACEHOLDER;
1344 case Node::STATIC: return TaskType::STATIC;
1345 case Node::RUNTIME: return TaskType::RUNTIME;
1346 case Node::NONPREEMPTIVE_RUNTIME: return TaskType::RUNTIME;
1347 case Node::SUBFLOW: return TaskType::SUBFLOW;
1348 case Node::CONDITION: return TaskType::CONDITION;
1349 case Node::MULTI_CONDITION: return TaskType::CONDITION;
1350 case Node::MODULE: return TaskType::MODULE;
1351 case Node::ADOPTED_MODULE: return TaskType::MODULE;
1352 case Node::ASYNC: return TaskType::ASYNC;
1353 case Node::DEPENDENT_ASYNC: return TaskType::ASYNC;
1354 default: return TaskType::UNDEFINED;
1355 }
1356}
1357
1358// Function: hash_value
1359inline size_t TaskView::hash_value() const {
1360 return std::hash<const Node*>{}(&_node);
1361}
1362
1363// Function: for_each_successor
1364template <typename V>
1365void TaskView::for_each_successor(V&& visitor) const {
1366 for(size_t i=0; i<_node._num_successors; ++i) {
1367 visitor(TaskView(*_node._edges[i]));
1368 }
1369 //for(size_t i=0; i<_node._successors.size(); ++i) {
1370 // visitor(TaskView(*_node._successors[i]));
1371 //}
1372}
1373
1374// Function: for_each_predecessor
1375template <typename V>
1376void TaskView::for_each_predecessor(V&& visitor) const {
1377 for(size_t i=_node._num_successors; i<_node._edges.size(); ++i) {
1378 visitor(TaskView(*_node._edges[i]));
1379 }
1380 //for(size_t i=0; i<_node._predecessors.size(); ++i) {
1381 // visitor(TaskView(*_node._predecessors[i]));
1382 //}
1383}
1384
1385} // end of namespace tf. ----------------------------------------------------
1386
1387namespace std {
1388
1394template <>
1395struct hash<tf::Task> {
1396 auto operator() (const tf::Task& task) const noexcept {
1397 return task.hash_value();
1398 }
1399};
1400
1406template <>
1407struct hash<tf::TaskView> {
1408 auto operator() (const tf::TaskView& task_view) const noexcept {
1409 return task_view.hash_value();
1410 }
1411};
1412
1413} // end of namespace std ----------------------------------------------------
class to create a graph object
Definition graph.hpp:47
class to create a semophore object for building a concurrency constraint
Definition semaphore.hpp:68
class to define a vector optimized for small array
Definition small_vector.hpp:931
class to access task information from the observer interface
Definition task.hpp:1240
size_t num_predecessors() const
queries the number of predecessors of the task
Definition task.hpp:1321
void for_each_predecessor(V &&visitor) const
applies an visitor callable to each predecessor of the task
Definition task.hpp:1376
void for_each_successor(V &&visitor) const
applies an visitor callable to each successor of the task
Definition task.hpp:1365
TaskType type() const
queries the task type
Definition task.hpp:1341
size_t num_weak_dependencies() const
queries the number of weak dependencies of the task
Definition task.hpp:1331
size_t hash_value() const
obtains a hash value of the underlying node
Definition task.hpp:1359
const std::string & name() const
queries the name of the task
Definition task.hpp:1316
size_t num_strong_dependencies() const
queries the number of strong dependencies of the task
Definition task.hpp:1326
size_t num_successors() const
queries the number of successors of the task
Definition task.hpp:1336
class to create a task handle over a taskflow node
Definition task.hpp:263
Task & acquire(Semaphore &semaphore)
makes the task acquire the given semaphore
Definition task.hpp:1024
const std::string & name() const
queries the name of the task
Definition task.hpp:1082
size_t num_strong_dependencies() const
queries the number of strong dependencies of the task
Definition task.hpp:1092
Task & remove_successors(Ts &&... tasks)
removes successor links from this to other tasks
Definition task.hpp:976
size_t num_successors() const
queries the number of successors of the task
Definition task.hpp:1102
size_t hash_value() const
obtains a hash value of the underlying node
Definition task.hpp:1171
void for_each_subflow_task(V &&visitor) const
applies an visitor callable to each subflow task
Definition task.hpp:1162
Task & release(Semaphore &semaphore)
makes the task release the given semaphore
Definition task.hpp:1048
Task & work(C &&callable)
assigns a callable
Definition task.hpp:1185
std::exception_ptr exception_ptr() const
retrieves the exception pointer of this task
Definition task.hpp:1117
void reset()
resets the task handle to null
Definition task.hpp:1072
void for_each_predecessor(V &&visitor) const
applies an visitor callable to each predecessor of the task
Definition task.hpp:1154
void * data() const
queries pointer to user data
Definition task.hpp:1209
void dump(std::ostream &ostream) const
dumps the task through an output stream
Definition task.hpp:1176
Task & succeed(Ts &&... tasks)
adds precedence links from other tasks to this
Definition task.hpp:960
Task & operator=(const Task &other)
replaces the contents with a copy of the other task
Definition task.hpp:996
Task()=default
constructs an empty task
bool empty() const
queries if the task handle is associated with a taskflow node
Definition task.hpp:1107
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition task.hpp:952
bool has_exception_ptr() const
queries if the task has an exception pointer
Definition task.hpp:1122
Task & adopt(tf::Graph &&graph)
creates a module task from a graph by taking over its ownership
Definition task.hpp:990
Task & composed_of(T &object)
creates a module task from a taskflow
Definition task.hpp:984
Task & remove_predecessors(Ts &&... tasks)
removes predecessor links from other tasks to this
Definition task.hpp:968
size_t num_weak_dependencies() const
queries the number of weak dependencies of the task
Definition task.hpp:1097
bool operator==(const Task &rhs) const
compares if two tasks are associated with the same taskflow node
Definition task.hpp:1008
size_t num_predecessors() const
queries the number of predecessors of the task
Definition task.hpp:1087
void reset_work()
resets the associated work to a placeholder
Definition task.hpp:1077
TaskType type() const
returns the task type
Definition task.hpp:1127
bool operator!=(const Task &rhs) const
compares if two tasks are not associated with the same taskflow node
Definition task.hpp:1013
bool has_work() const
queries if the task has a work assigned
Definition task.hpp:1112
Task & data(void *data)
assigns pointer to user data
Definition task.hpp:1214
void for_each_successor(V &&visitor) const
applies an visitor callable to each successor of the task
Definition task.hpp:1146
determines if a callable is a condition task
Definition task.hpp:168
determines if a callable is a multi-condition task
Definition task.hpp:188
determines if a callable is a runtime task
Definition task.hpp:141
determines if a callable is a static task
Definition task.hpp:94
determines if a callable is a subflow task
Definition task.hpp:117
taskflow namespace
Definition small_vector.hpp:20
constexpr bool is_condition_task_v
determines if a callable is a condition task (variable template)
Definition task.hpp:179
constexpr bool is_static_task_v
determines if a callable is a static task (variable template)
Definition task.hpp:105
TaskType
enumeration of all task types
Definition task.hpp:21
@ UNDEFINED
undefined task type (for internal use only)
Definition task.hpp:37
@ MODULE
module task type
Definition task.hpp:33
@ SUBFLOW
dynamic (subflow) task type
Definition task.hpp:29
@ CONDITION
condition task type
Definition task.hpp:31
@ ASYNC
asynchronous task type
Definition task.hpp:35
@ PLACEHOLDER
placeholder task type
Definition task.hpp:23
@ RUNTIME
runtime task type
Definition task.hpp:27
@ STATIC
static task type
Definition task.hpp:25
const char * to_string(TaskType type)
convert a task type to a human-readable string
Definition task.hpp:66
constexpr bool is_multi_condition_task_v
determines if a callable is a multi-condition task (variable template)
Definition task.hpp:199
Graph & retrieve_graph(T &target)
retrieves a reference to the underlying tf::Graph from an object
Definition graph.hpp:1098
std::ostream & operator<<(std::ostream &os, const Task &task)
overload of ostream inserter operator for Task
Definition task.hpp:1226
constexpr bool is_subflow_task_v
determines if a callable is a subflow task (variable template)
Definition task.hpp:128
constexpr bool is_runtime_task_v
determines if a callable is a runtime task (variable template)
Definition task.hpp:155
hash specialization for std::hash<tf::Task>