Taskflow  3.2.0-Master-Branch
Loading...
Searching...
No Matches
core/taskflow.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "flow_builder.hpp"
4
10namespace tf {
11
12// ----------------------------------------------------------------------------
13
73class Taskflow : public FlowBuilder {
74
75 friend class Topology;
76 friend class Executor;
77 friend class FlowBuilder;
78
79 struct Dumper {
80 size_t id;
83 };
84
85 public:
86
96
100 Taskflow();
101
117 Taskflow(Taskflow&& rhs);
118
135
164 ~Taskflow() = default;
165
189 void dump(std::ostream& ostream) const;
190
197 std::string dump() const;
198
202 size_t num_tasks() const;
203
210 bool empty() const;
211
219 void name(const std::string&);
220
228 const std::string& name() const;
229
237 void clear();
238
252 template <typename V>
253 void for_each_task(V&& visitor) const;
254
262 Graph& graph();
263
264 private:
265
266 mutable std::mutex _mutex;
267
268 std::string _name;
269
270 Graph _graph;
271
273
275
276 void _dump(std::ostream&, const Graph*) const;
277 void _dump(std::ostream&, const Node*, Dumper&) const;
278 void _dump(std::ostream&, const Graph*, Dumper&) const;
279};
280
281// Constructor
282inline Taskflow::Taskflow(const std::string& name) :
283 FlowBuilder {_graph},
284 _name {name} {
285}
286
287// Constructor
288inline Taskflow::Taskflow() : FlowBuilder{_graph} {
289}
290
291// Move constructor
292inline Taskflow::Taskflow(Taskflow&& rhs) : FlowBuilder{_graph} {
293
294 std::scoped_lock<std::mutex> lock(rhs._mutex);
295
296 _name = std::move(rhs._name);
297 _graph = std::move(rhs._graph);
298 _topologies = std::move(rhs._topologies);
299 _satellite = rhs._satellite;
300
301 rhs._satellite.reset();
302}
303
304// Move assignment
306 if(this != &rhs) {
307 std::scoped_lock<std::mutex, std::mutex> lock(_mutex, rhs._mutex);
308 _name = std::move(rhs._name);
309 _graph = std::move(rhs._graph);
310 _topologies = std::move(rhs._topologies);
311 _satellite = rhs._satellite;
312 rhs._satellite.reset();
313 }
314 return *this;
315}
316
317// Procedure:
318inline void Taskflow::clear() {
319 _graph._clear();
320}
321
322// Function: num_tasks
323inline size_t Taskflow::num_tasks() const {
324 return _graph.size();
325}
326
327// Function: empty
328inline bool Taskflow::empty() const {
329 return _graph.empty();
330}
331
332// Function: name
333inline void Taskflow::name(const std::string &name) {
334 _name = name;
335}
336
337// Function: name
338inline const std::string& Taskflow::name() const {
339 return _name;
340}
341
342// Function: graph
344 return _graph;
345}
346
347// Function: for_each_task
348template <typename V>
349void Taskflow::for_each_task(V&& visitor) const {
350 for(size_t i=0; i<_graph._nodes.size(); ++i) {
351 visitor(Task(_graph._nodes[i]));
352 }
353}
354
355// Procedure: dump
358 dump(oss);
359 return oss.str();
360}
361
362// Function: dump
363inline void Taskflow::dump(std::ostream& os) const {
364 os << "digraph Taskflow {\n";
365 _dump(os, &_graph);
366 os << "}\n";
367}
368
369// Procedure: _dump
370inline void Taskflow::_dump(std::ostream& os, const Graph* top) const {
371
372 Dumper dumper;
373
374 dumper.id = 0;
375 dumper.stack.push({nullptr, top});
376 dumper.visited[top] = dumper.id++;
377
378 while(!dumper.stack.empty()) {
379
380 auto [p, f] = dumper.stack.top();
381 dumper.stack.pop();
382
383 os << "subgraph cluster_p" << f << " {\nlabel=\"";
384
385 // n-level module
386 if(p) {
387 os << 'm' << dumper.visited[f];
388 }
389 // top-level taskflow graph
390 else {
391 os << "Taskflow: ";
392 if(_name.empty()) os << 'p' << this;
393 else os << _name;
394 }
395
396 os << "\";\n";
397
398 _dump(os, f, dumper);
399 os << "}\n";
400 }
401}
402
403// Procedure: _dump
404inline void Taskflow::_dump(
405 std::ostream& os, const Node* node, Dumper& dumper
406) const {
407
408 os << 'p' << node << "[label=\"";
409 if(node->_name.empty()) os << 'p' << node;
410 else os << node->_name;
411 os << "\" ";
412
413 // shape for node
414 switch(node->_handle.index()) {
415
416 case Node::CONDITION:
417 case Node::MULTI_CONDITION:
418 os << "shape=diamond color=black fillcolor=aquamarine style=filled";
419 break;
420
421 case Node::RUNTIME:
422 os << "shape=component";
423 break;
424
425 case Node::CUDAFLOW:
426 os << " style=\"filled\""
427 << " color=\"black\" fillcolor=\"purple\""
428 << " fontcolor=\"white\""
429 << " shape=\"folder\"";
430 break;
431
432 case Node::SYCLFLOW:
433 os << " style=\"filled\""
434 << " color=\"black\" fillcolor=\"red\""
435 << " fontcolor=\"white\""
436 << " shape=\"folder\"";
437 break;
438
439 default:
440 break;
441 }
442
443 os << "];\n";
444
445 for(size_t s=0; s<node->_successors.size(); ++s) {
446 if(node->_is_conditioner()) {
447 // case edge is dashed
448 os << 'p' << node << " -> p" << node->_successors[s]
449 << " [style=dashed label=\"" << s << "\"];\n";
450 } else {
451 os << 'p' << node << " -> p" << node->_successors[s] << ";\n";
452 }
453 }
454
455 // subflow join node
456 if(node->_parent && node->_parent->_handle.index() == Node::DYNAMIC &&
457 node->_successors.size() == 0
458 ) {
459 os << 'p' << node << " -> p" << node->_parent << ";\n";
460 }
461
462 // node info
463 switch(node->_handle.index()) {
464
465 case Node::DYNAMIC: {
466 auto& sbg = std::get_if<Node::Dynamic>(&node->_handle)->subgraph;
467 if(!sbg.empty()) {
468 os << "subgraph cluster_p" << node << " {\nlabel=\"Subflow: ";
469 if(node->_name.empty()) os << 'p' << node;
470 else os << node->_name;
471
472 os << "\";\n" << "color=blue\n";
473 _dump(os, &sbg, dumper);
474 os << "}\n";
475 }
476 }
477 break;
478
479 case Node::CUDAFLOW: {
480 std::get_if<Node::cudaFlow>(&node->_handle)->graph->dump(
481 os, node, node->_name
482 );
483 }
484 break;
485
486 case Node::SYCLFLOW: {
487 std::get_if<Node::syclFlow>(&node->_handle)->graph->dump(
488 os, node, node->_name
489 );
490 }
491 break;
492
493 default:
494 break;
495 }
496}
497
498// Procedure: _dump
499inline void Taskflow::_dump(
500 std::ostream& os, const Graph* graph, Dumper& dumper
501) const {
502
503 for(const auto& n : graph->_nodes) {
504
505 // regular task
506 if(n->_handle.index() != Node::MODULE) {
507 _dump(os, n, dumper);
508 }
509 // module task
510 else {
511 //auto module = &(std::get_if<Node::Module>(&n->_handle)->module);
512 auto module = &(std::get_if<Node::Module>(&n->_handle)->graph);
513
514 os << 'p' << n << "[shape=box3d, color=blue, label=\"";
515 if(n->_name.empty()) os << 'p' << n;
516 else os << n->_name;
517
518 if(dumper.visited.find(module) == dumper.visited.end()) {
519 dumper.visited[module] = dumper.id++;
520 dumper.stack.push({n, module});
521 }
522
523 os << " [m" << dumper.visited[module] << "]\"];\n";
524
525 for(const auto s : n->_successors) {
526 os << 'p' << n << "->" << 'p' << s << ";\n";
527 }
528 }
529 }
530}
531
532// ----------------------------------------------------------------------------
533// class definition: Future
534// ----------------------------------------------------------------------------
535
570template <typename T>
571class Future : public std::future<T> {
572
573 friend class Executor;
574 friend class Subflow;
575
576 using handle_t = std::variant<
578 >;
579
580 // variant index
581 constexpr static auto ASYNC = get_index_v<std::weak_ptr<AsyncTopology>, handle_t>;
582 constexpr static auto TASKFLOW = get_index_v<std::weak_ptr<Topology>, handle_t>;
583
584 public:
585
589 Future() = default;
590
594 Future(const Future&) = delete;
595
599 Future(Future&&) = default;
600
604 Future& operator = (const Future&) = delete;
605
609 Future& operator = (Future&&) = default;
610
623 bool cancel();
624
625 private:
626
627 handle_t _handle;
628
629 template <typename P>
630 Future(std::future<T>&&, P&&);
631};
632
633template <typename T>
634template <typename P>
636 std::future<T> {std::move(fu)},
637 _handle {std::forward<P>(p)} {
638}
639
640// Function: cancel
641template <typename T>
643 return std::visit([](auto&& arg){
644 using P = std::decay_t<decltype(arg)>;
646 return false;
647 }
648 else {
649 auto ptr = arg.lock();
650 if(ptr) {
651 ptr->_is_cancelled.store(true, std::memory_order_relaxed);
652 return true;
653 }
654 return false;
655 }
656 }, _handle);
657}
658
659
660} // end of namespace tf. ---------------------------------------------------
class to create an executor for running a taskflow graph
Definition executor.hpp:50
class to build a task dependency graph
Definition flow_builder.hpp:21
class to access the result of an execution
Definition core/taskflow.hpp:571
bool cancel()
cancels the execution of the running taskflow associated with this future object
Definition core/taskflow.hpp:642
Future()=default
default constructor
Future(const Future &)=delete
disabled copy constructor
Future(Future &&)=default
default move constructor
class to create a graph object
Definition graph.hpp:56
bool empty() const
queries if the graph is empty
Definition graph.hpp:770
size_t size() const
queries the number of nodes in the graph
Definition graph.hpp:765
class to construct a subflow graph from the execution of a dynamic task
Definition flow_builder.hpp:889
class to create a task handle over a node in a taskflow graph
Definition task.hpp:187
class to create a taskflow object
Definition core/taskflow.hpp:73
void clear()
clears the associated task dependency graph
Definition core/taskflow.hpp:318
bool empty() const
queries the emptiness of the taskflow
Definition core/taskflow.hpp:328
void for_each_task(V &&visitor) const
applies a visitor to each task in the taskflow
Definition core/taskflow.hpp:349
Graph & graph()
returns a reference to the underlying graph object
Definition core/taskflow.hpp:343
const std::string & name() const
queries the name of the taskflow
Definition core/taskflow.hpp:338
std::string dump() const
dumps the taskflow to a std::string of DOT format
Definition core/taskflow.hpp:356
Taskflow & operator=(Taskflow &&rhs)
move assignment operator
Definition core/taskflow.hpp:305
~Taskflow()=default
default destructor
Taskflow()
constructs a taskflow
Definition core/taskflow.hpp:288
size_t num_tasks() const
queries the number of tasks
Definition core/taskflow.hpp:323
flow builder include file
T forward(T... args)
T move(T... args)
taskflow namespace
Definition small_vector.hpp:27
@ ASYNC
asynchronous task type