3#include "../utility/macros.hpp"
4#include "../utility/traits.hpp"
5#include "../utility/iterator.hpp"
7#ifdef TF_ENABLE_TASK_POOL
8#include "../utility/object_pool.hpp"
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"
17#include "declarations.hpp"
18#include "environment.hpp"
19#include "semaphore.hpp"
20#include "topology.hpp"
50 friend class FlowBuilder;
52 friend class Taskflow;
53 friend class Executor;
124 std::vector<Node*> _nodes;
131 template <
typename ...ArgsT>
132 Node* _emplace_back(ArgsT&&...);
188concept StringLike = std::convertible_to<T, std::string_view>;
262 friend class NonpreemptiveRuntime;
263 friend class ExplicitAnchorGuard;
265 friend class Algorithm;
269#ifdef TF_ENABLE_TASK_PRIORITY
270 size_t priority()
const {
271 return static_cast<size_t>((_nstate & NSTATE::PRIORITY_MASK) >> NSTATE::PRIORITY_SHIFT);
277 nstate_t _nstate {NSTATE::NONE};
278 std::atomic<estate_t> _estate {ESTATE::NONE};
280 NodeBase* _parent {
nullptr};
281 std::atomic<size_t> _join_counter {0};
283 std::exception_ptr _exception_ptr {
nullptr};
285 NodeBase() =
default;
287 NodeBase(nstate_t nstate, estate_t estate, NodeBase* parent,
size_t join_counter) :
291 _join_counter {join_counter} {
294#ifdef TF_ENABLE_TASK_PRIORITY
296 nstate_t encoded = (
static_cast<nstate_t
>(p)) << NSTATE::PRIORITY_SHIFT;
297 _nstate = (_nstate & ~NSTATE::PRIORITY_MASK) | encoded;
301 void _rethrow_exception() {
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);
318class Topology :
public NodeBase {
320 friend class Executor;
321 friend class Subflow;
322 friend class Runtime;
323 friend class NonpreemptiveRuntime;
326 template <
typename T>
331 template <
typename Predicate,
typename OnFinish>
332 Topology(Taskflow&, Predicate&&, OnFinish&&);
334 bool cancelled()
const;
340 std::promise<void> _promise;
342 std::function<bool()> _predicate;
343 std::function<void()> _on_finish;
345 void _carry_out_promise();
349template <
typename Predicate,
typename OnFinish>
350Topology::Topology(
Taskflow& tf, Predicate&& predicate, OnFinish&& on_finish):
351 NodeBase(NSTATE::NONE, ESTATE::EXPLICITLY_ANCHORED, nullptr, 0),
353 _predicate(std::forward<Predicate>(predicate)),
354 _on_finish(std::forward<OnFinish> (on_finish)) {
358inline void Topology::_carry_out_promise() {
360 auto e = _exception_ptr;
361 _exception_ptr =
nullptr;
362 _promise.set_exception(e);
365 _promise.set_value();
370inline bool Topology::cancelled()
const {
371 return _estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION);
382class Node :
public NodeBase {
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;
398 using Placeholder = std::monostate;
403 template <
typename C>
406 std::function<void()> work;
412 template <
typename C>
415 std::function<void(tf::Runtime&)> work;
418 struct NonpreemptiveRuntime {
420 template <
typename C>
421 NonpreemptiveRuntime(C&&);
423 std::function<void(tf::NonpreemptiveRuntime&)> work;
429 template <
typename C>
432 std::function<void(tf::Subflow&)> work;
439 template <
typename C>
442 std::function<int()> work;
446 struct MultiCondition {
448 template <
typename C>
451 std::function<SmallVector<int>()> work;
463 struct AdoptedModule {
465 AdoptedModule(Graph&&);
473 template <
typename T>
477 std::function<void()>,
478 std::function<void(tf::Runtime&)>,
479 std::function<void(tf::Runtime&,
bool)>
484 struct DependentAsync {
486 template <
typename C>
490 std::function<void()>,
491 std::function<void(tf::Runtime&)>,
492 std::function<void(tf::Runtime&,
bool)>
500 using handle_t = std::variant<
504 NonpreemptiveRuntime,
515 SmallVector<Semaphore*> to_acquire;
516 SmallVector<Semaphore*> to_release;
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>;
536 template <
typename... Args>
537 Node(nstate_t, estate_t,
const TaskParams&, Topology*, NodeBase*,
size_t, Args&&...);
539 template <
typename... Args>
540 Node(nstate_t, estate_t,
const DefaultTaskParams&, Topology*, NodeBase*,
size_t, Args&&...);
542 template <StringLike S,
typename... Args>
543 Node(nstate_t, estate_t, S&&, Topology*, NodeBase*,
size_t, Args&&...);
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;
550 const std::string& name()
const;
556 void* _data {
nullptr};
558 Topology* _topology {
nullptr};
560 size_t _num_successors {0};
561 SmallVector<Node*, 4> _edges;
565 std::unique_ptr<Semaphores> _semaphores;
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();
574 void _remove_successors(Node*);
575 void _remove_predecessors(Node*);
585Node::Static::Static(C&& c) : work {std::forward<C>(c)} {
594Node::Runtime::Runtime(C&& c) : work {std::forward<C>(c)} {
599Node::NonpreemptiveRuntime::NonpreemptiveRuntime(C&& c) : work {std::forward<C>(c)} {
608Node::Subflow::Subflow(C&& c) : work {std::forward<C>(c)} {
617Node::Condition::Condition(C&& c) : work {std::forward<C>(c)} {
626Node::MultiCondition::MultiCondition(C&& c) : work {std::forward<C>(c)} {
634inline Node::Module::Module(Graph& g) : graph(g){
638inline Node::AdoptedModule::AdoptedModule(Graph&& g) : graph(std::move(g)){
647Node::Async::Async(C&& c) : work {std::forward<C>(c)} {
656Node::DependentAsync::DependentAsync(C&& c) : work {std::forward<C>(c)} {
664template <
typename... Args>
668 const TaskParams& params,
674 NodeBase(nstate, estate, parent, join_counter),
677 _topology {topology},
678 _handle {std::forward<Args>(args)...} {
682template <
typename... Args>
686 const DefaultTaskParams&,
692 NodeBase(nstate, estate, parent, join_counter),
693 _topology {topology},
694 _handle {std::forward<Args>(args)...} {
698template <StringLike S,
typename... Args>
708 NodeBase(nstate, estate, parent, join_counter),
709 _name {std::forward<S>(name)},
710 _topology {topology},
711 _handle {std::forward<Args>(args)...} {
778inline void Node::_precede(Node* v) {
780 std::swap(_edges[_num_successors++], _edges[_edges.size() - 1]);
781 v->_edges.push_back(
this);
785inline 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;
794inline void Node::_remove_predecessors(Node* node) {
796 std::remove(_edges.begin() + _num_successors, _edges.end(), node), _edges.end()
801inline size_t Node::num_successors()
const {
802 return _num_successors;
806inline size_t Node::num_predecessors()
const {
807 return _edges.size() - _num_successors;
811inline size_t Node::num_weak_dependencies()
const {
813 for(
size_t i=_num_successors; i<_edges.size(); i++) {
814 n += _edges[i]->_is_conditioner();
820inline size_t Node::num_strong_dependencies()
const {
822 for(
size_t i=_num_successors; i<_edges.size(); i++) {
823 n += !_edges[i]->_is_conditioner();
829inline const std::string& Node::name()
const {
834inline bool Node::_is_conditioner()
const {
835 return _handle.index() == Node::CONDITION ||
836 _handle.index() == Node::MULTI_CONDITION;
840inline bool Node::_is_parent_cancelled()
const {
841 return (_topology && (_topology->_estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION)))
843 (_parent && (_parent->_estate.load(std::memory_order_relaxed) & (ESTATE::CANCELLED | ESTATE::EXCEPTION)));
847inline void Node::_set_up_join_counter() {
849 for(
size_t i=_num_successors; i<_edges.size(); i++) {
850 _nstate += !_edges[i]->_is_conditioner();
852 _join_counter.store(_nstate & NSTATE::STRONG_DEPENDENCIES_MASK, std::memory_order_relaxed);
857inline bool Node::_acquire_all(SmallVector<Node*>& nodes) {
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);
872inline void Node::_release_all(SmallVector<Node*>& nodes) {
874 auto& to_release = _semaphores->to_release;
875 for(
const auto& sem : to_release) {
876 sem->_release(nodes);
889class ExplicitAnchorGuard {
895 ExplicitAnchorGuard(NodeBase* node_base) : _node_base{node_base} {
896 _node_base->_estate.fetch_or(ESTATE::EXPLICITLY_ANCHORED, std::memory_order_relaxed);
899 ~ExplicitAnchorGuard() {
900 _node_base->_estate.fetch_and(~ESTATE::EXPLICITLY_ANCHORED, std::memory_order_relaxed);
905 NodeBase* _node_base;
912#ifdef TF_ENABLE_TASK_POOL
916using NodePool = std::conditional_t<
917 std::atomic<tf::TaggedHead128>::is_always_lock_free,
918 ObjectPool<Node, tf::TaggedHead128>,
919 ObjectPool<Node, tf::TaggedHead64<>>
921inline NodePool _node_pool;
927template <
typename... ArgsT>
928TF_FORCE_INLINE Node* animate(ArgsT&&... args) {
929#ifdef TF_ENABLE_TASK_POOL
930 return _node_pool.animate(std::forward<ArgsT>(args)...);
932 return new Node(std::forward<ArgsT>(args)...);
939TF_FORCE_INLINE
void recycle(Node* ptr) {
940#ifdef TF_ENABLE_TASK_POOL
941 _node_pool.recycle(ptr);
959 _nodes {std::move(other._nodes)} {
965 _nodes = std::move(other._nodes);
971 for(
auto node : _nodes) {
979 return _nodes.size();
984 return _nodes.empty();
989 return _nodes.begin();
999 return _nodes.begin();
1004 return _nodes.end();
1008inline void Graph::_erase(Node* node) {
1014 std::remove_if(_nodes.begin(), _nodes.end(), [&](
auto& p){
1028template <
typename ...ArgsT>
1029Node* Graph::_emplace_back(ArgsT&&... args) {
1030 _nodes.push_back(animate(std::forward<ArgsT>(args)...));
1031 return _nodes.back();
1068template <
typename T>
1071 { t.graph() } -> std::convertible_to<Graph&>;
1107template <GraphLike T>
1109 if constexpr (
requires { target.graph(); }) {
1110 return target.graph();
1112 return static_cast<Graph&
>(target);
class to hold a dependent asynchronous task with shared ownership
Definition async_task.hpp:45
class to create an empty task parameter for compile-time optimization
Definition graph.hpp:217
class to create an executor
Definition executor.hpp:62
class to build a task dependency graph
Definition flow_builder.hpp:114
class to create a graph object
Definition graph.hpp:47
Graph & operator=(const Graph &)=delete
disabled copy assignment operator
Graph()=default
constructs the graph object
bool empty() const
queries the emptiness of the graph
Definition graph.hpp:983
~Graph()
destroys the graph object
Definition graph.hpp:953
auto end()
returns an iterator past the last element of this graph
Definition graph.hpp:993
size_t size() const
returns the number of nodes in the graph
Definition graph.hpp:978
void clear()
clears the graph
Definition graph.hpp:970
auto begin()
returns an iterator to the first node of this graph
Definition graph.hpp:988
Graph(const Graph &)=delete
disabled copy constructor
class to create a runtime task
Definition runtime.hpp:47
class to construct a subflow graph from the execution of a dynamic task
Definition flow_builder.hpp:1913
class to create a task group from a task
Definition task_group.hpp:61
class to create a task parameter object
Definition graph.hpp:197
std::string name
name of the task
Definition graph.hpp:204
void * data
C-styled pointer to user data.
Definition graph.hpp:209
class to access task information from the observer interface
Definition task.hpp:1613
class to create a task handle over a taskflow node
Definition task.hpp:569
class to create a taskflow object
Definition taskflow.hpp:64
concept to check if a type owns or provides access to a tf::Graph
Definition graph.hpp:1069
concept to check if a type is convertible to std::string_view
Definition graph.hpp:188
concept to check if a type is a task parameter
Definition graph.hpp:228
taskflow namespace
Definition small_vector.hpp:20
@ 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
Graph & retrieve_graph(T &target)
retrieves a reference to the underlying tf::Graph from an object
Definition graph.hpp:1108
TaskPriority
enumeration of all task priority levels
Definition graph.hpp:152
@ NORMAL
the normal task priority level
Definition graph.hpp:156
@ LOW
the lowest task priority level (least urgent)
Definition graph.hpp:158
@ HIGH
the highest task priority level (most urgent)
Definition graph.hpp:154
constexpr bool is_task_params_v
concept that determines if a type is a task parameter type (variable template)
Definition graph.hpp:241