docs/task_8hpp_source.html
| | Taskflow: A General-purpose Task-parallel Programming System |
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
23PLACEHOLDER = 0,
38};
39
44inline constexpr std::array<TaskType, 7> TASK_TYPES = {
52};
53
66inline const char* to_string(TaskType type) {
67
68const char* val;
69
70switch(type) {
71case TaskType::PLACEHOLDER: val = "placeholder"; break;
72case TaskType::STATIC: val = "static"; break;
73case TaskType::RUNTIME: val = "runtime"; break;
74case TaskType::SUBFLOW: val = "subflow"; break;
75case TaskType::CONDITION: val = "condition"; break;
76case TaskType::MODULE: val = "module"; break;
77case TaskType::ASYNC: val = "async"; break;
78default: val = "undefined"; break;
79 }
80
81return val;
82}
83
84// ----------------------------------------------------------------------------
85// Static Task Trait
86// ----------------------------------------------------------------------------
87
139template <typename C>
140concept StaticTaskLike = std::invocable<C> &&
141 std::same_as<std::invoke_result_t<C>, void>;
142
150template <typename C>
151constexpr bool is_static_task_v = StaticTaskLike<C>;
152
153// ----------------------------------------------------------------------------
154// Subflow Task Trait
155// ----------------------------------------------------------------------------
156
219template <typename C>
220concept SubflowTaskLike = std::invocable<C, tf::Subflow&> &&
221 std::same_as<std::invoke_result_t<C, tf::Subflow&>, void>;
222
230template <typename C>
231constexpr bool is_subflow_task_v = SubflowTaskLike<C>;
232
233// ----------------------------------------------------------------------------
234// Runtime Task Trait
235// ----------------------------------------------------------------------------
236
302template <typename C>
303concept RuntimeTaskLike =
304 (std::invocable<C, tf::Runtime&> &&
305 std::same_as<std::invoke_result_t<C, tf::Runtime&>, void>) ||
306 (std::invocable<C, tf::NonpreemptiveRuntime&> &&
307 std::same_as<std::invoke_result_t<C, tf::NonpreemptiveRuntime&>, void>);
308
316template <typename C>
317constexpr bool is_runtime_task_v = RuntimeTaskLike<C>;
318
319
320// ----------------------------------------------------------------------------
321// Condition Task Trait
322// ----------------------------------------------------------------------------
323
396template <typename C>
397concept ConditionTaskLike = std::invocable<C> &&
398 std::convertible_to<std::invoke_result_t<C>, int>;
399
407template <typename C>
408constexpr bool is_condition_task_v = ConditionTaskLike<C>;
409
493template <typename C>
494concept MultiConditionTaskLike = std::invocable<C> &&
495 std::same_as<std::invoke_result_t<C>, SmallVector<int>>;
496
504template <typename C>
505constexpr bool is_multi_condition_task_v = MultiConditionTaskLike<C>;
506
507
508// ----------------------------------------------------------------------------
509// Task
510// ----------------------------------------------------------------------------
511
570
571friend class FlowBuilder;
572friend class Runtime;
573friend class NonpreemptiveRuntime;
574friend class Taskflow;
575friend class TaskView;
576friend class Executor;
577
578public:
579
586
600
612Task& operator =(const Task& other);
613
622Task& operator =(std::nullptr_t);
623
636bool operator ==(const Task& rhs) const;
637
650bool operator !=(const Task& rhs) const;
651
663const std::string& name() const;
664
677size_t num_successors() const;
678
691size_t num_predecessors() const;
692
720size_t num_strong_dependencies() const;
721
749size_t num_weak_dependencies() const;
750
763Task& name(const std::string& name);
764
787template <typename C>
789
807template <GraphLike T>
808Task& composed_of(T& object);
809
831Task& adopt(tf::Graph&& graph);
832
852template <typename... Ts>
853Task& precede(Ts&&... tasks);
854
874template <typename... Ts>
875Task& succeed(Ts&&... tasks);
876
904template <typename... Ts>
905Task& remove_predecessors(Ts&&... tasks);
906
934template <typename... Ts>
935Task& remove_successors(Ts&&... tasks);
936
943Task& release(Semaphore& semaphore);
944
951template <typename I>
952Task& release(I first, I last);
953
960Task& acquire(Semaphore& semaphore);
961
968template <typename I>
969Task& acquire(I first, I last);
970
1001
1014void reset();
1015
1019void reset_work();
1020
1035bool empty() const;
1036
1050bool has_work() const;
1051
1074template <typename V>
1075void for_each_successor(V&& visitor) const;
1076
1098template <typename V>
1099void for_each_predecessor(V&& visitor) const;
1100
1121template <typename V>
1122void for_each_subflow_task(V&& visitor) const;
1123
1136size_t hash_value() const;
1137
1151
1161void dump(std::ostream& ostream) const;
1162
1190void* data() const;
1191
1232 std::exception_ptr exception_ptr() const;
1233
1239bool has_exception_ptr() const;
1240
1241private:
1242
1243Task(Node*);
1244
1245 Node* _node {nullptr};
1246};
1247
1248// Constructor
1249inline Task::Task(Node* node) : _node {node} {
1250}
1251
1252// Constructor
1253inline Task::Task(const Task& rhs) : _node {rhs._node} {
1254}
1255
1256// Function: precede
1257template <typename... Ts>
1258Task& Task::precede(Ts&&... tasks) {
1259 (_node->_precede(tasks._node), ...);
1260//_precede(std::forward<Ts>(tasks)...);
1261return *this;
1262}
1263
1264// Function: succeed
1265template <typename... Ts>
1266Task& Task::succeed(Ts&&... tasks) {
1267 (tasks._node->_precede(_node), ...);
1268//_succeed(std::forward<Ts>(tasks)...);
1269return *this;
1270}
1271
1272// Function: remove_predecessors
1273template <typename... Ts>
1274Task& Task::remove_predecessors(Ts&&... tasks) {
1275 (tasks._node->_remove_successors(_node), ...);
1276 (_node->_remove_predecessors(tasks._node), ...);
1277return *this;
1278}
1279
1280// Function: remove_successors
1281template <typename... Ts>
1282Task& Task::remove_successors(Ts&&... tasks) {
1283 (_node->_remove_successors(tasks._node), ...);
1284 (tasks._node->_remove_predecessors(_node), ...);
1285return *this;
1286}
1287
1288// Function: composed_of
1289template <GraphLike T>
1290Task& Task::composed_of(T& target) {
1291 _node->_handle.emplace<Node::Module>(retrieve_graph(target));
1292return *this;
1293}
1294
1295// Function: adopt
1296inline Task& Task::adopt(Graph&& graph) {
1297 _node->_handle.emplace<Node::AdoptedModule>(std::move(graph));
1298return *this;
1299}
1300
1301// Operator =
1302inline Task& Task::operator =(const Task& rhs) {
1303 _node = rhs._node;
1304return *this;
1305}
1306
1307// Operator =
1308inline Task& Task::operator =(std::nullptr_t ptr) {
1309 _node = ptr;
1310return *this;
1311}
1312
1313// Operator ==
1314inline bool Task::operator ==(const Task& rhs) const {
1315return _node == rhs._node;
1316}
1317
1318// Operator !=
1319inline bool Task::operator !=(const Task& rhs) const {
1320return _node != rhs._node;
1321}
1322
1323// Function: name
1324inline Task& Task::name(const std::string& name) {
1325 _node->_name = name;
1326return *this;
1327}
1328
1329// Function: acquire
1330inline Task& Task::acquire(Semaphore& s) {
1331if(!_node->_semaphores) {
1332 _node->_semaphores = std::make_unique<Node::Semaphores>();
1333 }
1334 _node->_semaphores->to_acquire.push_back(&s);
1335return *this;
1336}
1337
1338// Function: acquire
1339template <typename I>
1340Task& Task::acquire(I first, I last) {
1341if(!_node->_semaphores) {
1342 _node->_semaphores = std::make_unique<Node::Semaphores>();
1343 }
1344 _node->_semaphores->to_acquire.reserve(
1345 _node->_semaphores->to_acquire.size() + std::distance(first, last)
1346 );
1347for(auto s = first; s != last; ++s){
1348 _node->_semaphores->to_acquire.push_back(&(*s));
1349 }
1350return *this;
1351}
1352
1353// Function: release
1354inline Task& Task::release(Semaphore& s) {
1355if(!_node->_semaphores) {
1356 _node->_semaphores = std::make_unique<Node::Semaphores>();
1357 }
1358 _node->_semaphores->to_release.push_back(&s);
1359return *this;
1360}
1361
1362// Function: release
1363template <typename I>
1364Task& Task::release(I first, I last) {
1365if(!_node->_semaphores) {
1366 _node->_semaphores = std::make_unique<Node::Semaphores>();
1367 }
1368 _node->_semaphores->to_release.reserve(
1369 _node->_semaphores->to_release.size() + std::distance(first, last)
1370 );
1371for(auto s = first; s != last; ++s) {
1372 _node->_semaphores->to_release.push_back(&(*s));
1373 }
1374return *this;
1375}
1376
1377// Procedure: reset
1378inline void Task::reset() {
1379 _node = nullptr;
1380}
1381
1382// Procedure: reset_work
1383inline void Task::reset_work() {
1384 _node->_handle.emplace<std::monostate>();
1385}
1386
1387// Function: name
1388inline const std::string& Task::name() const {
1389return _node->_name;
1390}
1391
1392// Function: num_predecessors
1393inline size_t Task::num_predecessors() const {
1394return _node->num_predecessors();
1395}
1396
1397// Function: num_strong_dependencies
1398inline size_t Task::num_strong_dependencies() const {
1399return _node->num_strong_dependencies();
1400}
1401
1402// Function: num_weak_dependencies
1403inline size_t Task::num_weak_dependencies() const {
1404return _node->num_weak_dependencies();
1405}
1406
1407// Function: num_successors
1408inline size_t Task::num_successors() const {
1409return _node->num_successors();
1410}
1411
1412// Function: empty
1413inline bool Task::empty() const {
1414return _node == nullptr;
1415}
1416
1417// Function: has_work
1418inline bool Task::has_work() const {
1419return _node ? _node->_handle.index() != 0 : false;
1420}
1421
1422// Function: exception
1423inline std::exception_ptr Task::exception_ptr() const {
1424return _node ? _node->_exception_ptr : nullptr;
1425}
1426
1427// Function: has_exception
1428inline bool Task::has_exception_ptr() const {
1429return _node ? (_node->_exception_ptr != nullptr) : false;
1430}
1431
1432// Function: task_type
1433inline TaskType Task::type() const {
1434switch(_node->_handle.index()) {
1435case Node::PLACEHOLDER: return TaskType::PLACEHOLDER;
1436case Node::STATIC: return TaskType::STATIC;
1437case Node::RUNTIME: return TaskType::RUNTIME;
1438case Node::NONPREEMPTIVE_RUNTIME: return TaskType::RUNTIME;
1439case Node::SUBFLOW: return TaskType::SUBFLOW;
1440case Node::CONDITION: return TaskType::CONDITION;
1441case Node::MULTI_CONDITION: return TaskType::CONDITION;
1442case Node::MODULE: return TaskType::MODULE;
1443case Node::ADOPTED_MODULE: return TaskType::MODULE;
1444case Node::ASYNC: return TaskType::ASYNC;
1445case Node::DEPENDENT_ASYNC: return TaskType::ASYNC;
1446default: return TaskType::UNDEFINED;
1447 }
1448}
1449
1450// Function: for_each_successor
1451template <typename V>
1452void Task::for_each_successor(V&& visitor) const {
1453for(size_t i=0; i<_node->_num_successors; ++i) {
1454 visitor(Task(_node->_edges[i]));
1455 }
1456}
1457
1458// Function: for_each_predecessor
1459template <typename V>
1460void Task::for_each_predecessor(V&& visitor) const {
1461for(size_t i=_node->_num_successors; i<_node->_edges.size(); ++i) {
1462 visitor(Task(_node->_edges[i]));
1463 }
1464}
1465
1466// Function: for_each_subflow_task
1467template <typename V>
1468void Task::for_each_subflow_task(V&& visitor) const {
1469if(auto ptr = std::get_if<Node::Subflow>(&_node->_handle); ptr) {
1470for(auto itr = ptr->subgraph.begin(); itr != ptr->subgraph.end(); ++itr) {
1471 visitor(Task(*itr));
1472 }
1473 }
1474}
1475
1476// Function: hash_value
1477inline size_t Task::hash_value() const {
1478return std::hash<Node*>{}(_node);
1479}
1480
1481// Procedure: dump
1482inline void Task::dump(std::ostream& os) const {
1483 os << "task ";
1484if(name().empty()) os << _node;
1485else os << name();
1486 os << " [type=" << to_string(type()) << ']';
1487}
1488
1489// Function: work
1490template <typename C>
1491Task& Task::work(C&& c) {
1492
1493if constexpr(is_static_task_v<C>) {
1494 _node->_handle.emplace<Node::Static>(std::forward<C>(c));
1495 }
1496else if constexpr(is_runtime_task_v<C>) {
1497 _node->_handle.emplace<Node::Runtime>(std::forward<C>(c));
1498 }
1499else if constexpr(is_subflow_task_v<C>) {
1500 _node->_handle.emplace<Node::Subflow>(std::forward<C>(c));
1501 }
1502else if constexpr(is_condition_task_v<C>) {
1503 _node->_handle.emplace<Node::Condition>(std::forward<C>(c));
1504 }
1505else if constexpr(is_multi_condition_task_v<C>) {
1506 _node->_handle.emplace<Node::MultiCondition>(std::forward<C>(c));
1507 }
1508else {
1509static_assert(dependent_false_v<C>, "invalid task callable");
1510 }
1511return *this;
1512}
1513
1514// Function: data
1515inline void* Task::data() const {
1516return _node->_data;
1517}
1518
1519// Function: data
1520inline Task& Task::data(void* data) {
1521 _node->_data = data;
1522return *this;
1523}
1524
1525// ----------------------------------------------------------------------------
1526// global ostream
1527// ----------------------------------------------------------------------------
1528
1532inline std::ostream& operator <<(std::ostream& os, const Task& task) {
1533 task.dump(os);
1534return os;
1535}
1536
1537// ----------------------------------------------------------------------------
1538// Task View
1539// ----------------------------------------------------------------------------
1540
1546class TaskView {
1547
1548friend class Executor;
1549
1550public:
1551
1555const std::string& name() const;
1556
1560size_t num_successors() const;
1561
1565size_t num_predecessors() const;
1566
1570size_t num_strong_dependencies() const;
1571
1575size_t num_weak_dependencies() const;
1576
1585template <typename V>
1586void for_each_successor(V&& visitor) const;
1587
1596template <typename V>
1597void for_each_predecessor(V&& visitor) const;
1598
1603
1607size_t hash_value() const;
1608
1609private:
1610
1611 TaskView(const Node&);
1612 TaskView(const TaskView&) = default;
1613
1614const Node& _node;
1615};
1616
1617// Constructor
1618inline TaskView::TaskView(const Node& node) : _node {node} {
1619}
1620
1621// Function: name
1622inline const std::string& TaskView::name() const {
1623return _node._name;
1624}
1625
1626// Function: num_predecessors
1627inline size_t TaskView::num_predecessors() const {
1628return _node.num_predecessors();
1629}
1630
1631// Function: num_strong_dependencies
1632inline size_t TaskView::num_strong_dependencies() const {
1633return _node.num_strong_dependencies();
1634}
1635
1636// Function: num_weak_dependencies
1637inline size_t TaskView::num_weak_dependencies() const {
1638return _node.num_weak_dependencies();
1639}
1640
1641// Function: num_successors
1642inline size_t TaskView::num_successors() const {
1643return _node.num_successors();
1644}
1645
1646// Function: type
1647inline TaskType TaskView::type() const {
1648switch(_node._handle.index()) {
1649case Node::PLACEHOLDER: return TaskType::PLACEHOLDER;
1650case Node::STATIC: return TaskType::STATIC;
1651case Node::RUNTIME: return TaskType::RUNTIME;
1652case Node::NONPREEMPTIVE_RUNTIME: return TaskType::RUNTIME;
1653case Node::SUBFLOW: return TaskType::SUBFLOW;
1654case Node::CONDITION: return TaskType::CONDITION;
1655case Node::MULTI_CONDITION: return TaskType::CONDITION;
1656case Node::MODULE: return TaskType::MODULE;
1657case Node::ADOPTED_MODULE: return TaskType::MODULE;
1658case Node::ASYNC: return TaskType::ASYNC;
1659case Node::DEPENDENT_ASYNC: return TaskType::ASYNC;
1660default: return TaskType::UNDEFINED;
1661 }
1662}
1663
1664// Function: hash_value
1665inline size_t TaskView::hash_value() const {
1666return std::hash<const Node*>{}(&_node);
1667}
1668
1669// Function: for_each_successor
1670template <typename V>
1671void TaskView::for_each_successor(V&& visitor) const {
1672for(size_t i=0; i<_node._num_successors; ++i) {
1673 visitor(TaskView(*_node._edges[i]));
1674 }
1675//for(size_t i=0; i<_node._successors.size(); ++i) {
1676// visitor(TaskView(*_node._successors[i]));
1677//}
1678}
1679
1680// Function: for_each_predecessor
1681template <typename V>
1682void TaskView::for_each_predecessor(V&& visitor) const {
1683for(size_t i=_node._num_successors; i<_node._edges.size(); ++i) {
1684 visitor(TaskView(*_node._edges[i]));
1685 }
1686//for(size_t i=0; i<_node._predecessors.size(); ++i) {
1687// visitor(TaskView(*_node._predecessors[i]));
1688//}
1689}
1690
1691} // end of namespace tf. ----------------------------------------------------
1692
1693namespace std {
1694
1745template <>
1747auto operator() (const tf::Task& task) const noexcept {
1748return task.hash_value();
1749 }
1750};
1751
1752
1753
1807template <>
1808struct hash<tf::TaskView> {
1809auto operator() (const tf::TaskView& task_view) const noexcept {
1810return task_view.hash_value();
1811 }
1812};
1813
1814} // 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:1546
tf::TaskView::num_predecessors
size_t num_predecessors() const
queries the number of predecessors of the task
Definition task.hpp:1627
tf::TaskView::for_each_predecessor
void for_each_predecessor(V &&visitor) const
applies an visitor callable to each predecessor of the task
Definition task.hpp:1682
tf::TaskView::for_each_successor
void for_each_successor(V &&visitor) const
applies an visitor callable to each successor of the task
Definition task.hpp:1671
TaskType type() const
queries the task type
Definition task.hpp:1647
tf::TaskView::num_weak_dependencies
size_t num_weak_dependencies() const
queries the number of weak dependencies of the task
Definition task.hpp:1637
size_t hash_value() const
obtains a hash value of the underlying node
Definition task.hpp:1665
const std::string & name() const
queries the name of the task
Definition task.hpp:1622
tf::TaskView::num_strong_dependencies
size_t num_strong_dependencies() const
queries the number of strong dependencies of the task
Definition task.hpp:1632
size_t num_successors() const
queries the number of successors of the task
Definition task.hpp:1642
class to create a task handle over a taskflow node
Definition task.hpp:569
Task & acquire(Semaphore &semaphore)
makes the task acquire the given semaphore
Definition task.hpp:1330
const std::string & name() const
queries the name of the task
Definition task.hpp:1388
tf::Task::num_strong_dependencies
size_t num_strong_dependencies() const
queries the number of strong dependencies of the task
Definition task.hpp:1398
Task & remove_successors(Ts &&... tasks)
removes successor links from this to other tasks
Definition task.hpp:1282
size_t num_successors() const
queries the number of successors of the task
Definition task.hpp:1408
size_t hash_value() const
obtains a hash value of the underlying node
Definition task.hpp:1477
tf::Task::for_each_subflow_task
void for_each_subflow_task(V &&visitor) const
applies an visitor callable to each subflow task
Definition task.hpp:1468
Task & release(Semaphore &semaphore)
makes the task release the given semaphore
Definition task.hpp:1354
Task & work(C &&callable)
assigns a callable
Definition task.hpp:1491
std::exception_ptr exception_ptr() const
retrieves the exception pointer of this task
Definition task.hpp:1423
void reset()
resets the task handle to null
Definition task.hpp:1378
tf::Task::for_each_predecessor
void for_each_predecessor(V &&visitor) const
applies an visitor callable to each predecessor of the task
Definition task.hpp:1460
void * data() const
queries pointer to user data
Definition task.hpp:1515
void dump(std::ostream &ostream) const
dumps the task through an output stream
Definition task.hpp:1482
Task & succeed(Ts &&... tasks)
adds precedence links from other tasks to this
Definition task.hpp:1266
Task & operator=(const Task &other)
replaces the contents with a copy of the other task
Definition task.hpp:1302
Task()=default
constructs an empty task
bool empty() const
queries if the task handle is associated with a taskflow node
Definition task.hpp:1413
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition task.hpp:1258
bool has_exception_ptr() const
queries if the task has an exception pointer
Definition task.hpp:1428
Task & adopt(tf::Graph &&graph)
creates a module task from a graph by taking over its ownership
Definition task.hpp:1296
Task & composed_of(T &object)
creates a module task from a taskflow
Definition task.hpp:1290
Task & remove_predecessors(Ts &&... tasks)
removes predecessor links from other tasks to this
Definition task.hpp:1274
tf::Task::num_weak_dependencies
size_t num_weak_dependencies() const
queries the number of weak dependencies of the task
Definition task.hpp:1403
bool operator==(const Task &rhs) const
compares if two tasks are associated with the same taskflow node
Definition task.hpp:1314
size_t num_predecessors() const
queries the number of predecessors of the task
Definition task.hpp:1393
void reset_work()
resets the associated work to a placeholder
Definition task.hpp:1383
TaskType type() const
returns the task type
Definition task.hpp:1433
bool operator!=(const Task &rhs) const
compares if two tasks are not associated with the same taskflow node
Definition task.hpp:1319
bool has_work() const
queries if the task has a work assigned
Definition task.hpp:1418
Task & data(void *data)
assigns pointer to user data
Definition task.hpp:1520
void for_each_successor(V &&visitor) const
applies an visitor callable to each successor of the task
Definition task.hpp:1452
determines if a callable is a condition task
Definition task.hpp:397
determines if a callable is a multi-condition task
Definition task.hpp:494
determines if a callable is a runtime task
Definition task.hpp:303
determines if a callable is a static task
Definition task.hpp:140
determines if a callable is a subflow task
Definition task.hpp:220
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:408
constexpr bool is_static_task_v
determines if a callable is a static task (variable template)
Definition task.hpp:151
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:505
Graph & retrieve_graph(T &target)
retrieves a reference to the underlying tf::Graph from an object
Definition graph.hpp:1067
std::ostream & operator<<(std::ostream &os, const Task &task)
overload of ostream inserter operator for Task
Definition task.hpp:1532
constexpr bool is_subflow_task_v
determines if a callable is a subflow task (variable template)
Definition task.hpp:231
constexpr bool is_runtime_task_v
determines if a callable is a runtime task (variable template)
Definition task.hpp:317
hash specialization for std::hash<tf::Task>