51 friend class PreemptionGuard;
52 friend class Algorithm;
156 template <
typename F>
179 template <TaskParamsLike P,
typename F>
180 auto async(P&& params, F&& f);
206 template <
typename F>
227 template <TaskParamsLike P,
typename F>
349 template <
typename F, std::input_iterator I>
393 template <TaskParamsLike P,
typename F, std::input_iterator I>
497 template <
typename F, std::input_iterator I>
535 template <TaskParamsLike P,
typename F, std::input_iterator I>
610inline Runtime::Runtime(
Executor& executor,
Worker& worker, Node* node) :
611 _executor {executor},
629 auto node = task._node;
633 node->_join_counter.store(0, std::memory_order_relaxed);
635 auto& j = node->_parent ? node->_parent->_join_counter :
636 node->_topology->_join_counter;
637 j.fetch_add(1, std::memory_order_relaxed);
638 _executor._schedule(_worker, node);
644 ExplicitAnchorGuard anchor(_node);
645 _executor._corun_until(_worker, [
this] () ->
bool {
646 return _node->_join_counter.load(std::memory_order_acquire) == 1;
649 _node->_rethrow_exception();
658 return _node->_is_parent_cancelled();
672template <TaskParamsLike P,
typename F>
674 _node->_join_counter.fetch_add(1, std::memory_order_relaxed);
675 _executor._silent_async(
676 std::forward<P>(params), std::forward<F>(f), _node->_topology, _node
691template <TaskParamsLike P,
typename F>
693 _node->_join_counter.fetch_add(1, std::memory_order_relaxed);
694 return _executor._async(
695 std::forward<P>(params), std::forward<F>(f), _node->_topology, _node
714 P&& params, F&& func, Tasks&&... tasks
716 std::array<
AsyncTask*,
sizeof...(Tasks)> array = { (&tasks)... };
718 std::forward<P>(params), std::forward<F>(func), array.begin(), array.end()
723template <
typename F, std::input_iterator I>
729template <TaskParamsLike P,
typename F, std::input_iterator I>
731 P&& params, F&& func, I first, I last
733 _node->_join_counter.fetch_add(1, std::memory_order_relaxed);
734 return _executor._silent_dependent_async(
735 std::forward<P>(params), std::forward<F>(func), first, last, _node->_topology, _node
752 std::array<
AsyncTask*,
sizeof...(Tasks)> array = { (&tasks)... };
754 std::forward<P>(params), std::forward<F>(func), array.begin(), array.end()
759template <
typename F, std::input_iterator I>
765template <TaskParamsLike P,
typename F, std::input_iterator I>
767 _node->_join_counter.fetch_add(1, std::memory_order_relaxed);
768 return _executor._dependent_async(
769 std::forward<P>(params), std::forward<F>(func), first, last, _node->_topology, _node
778inline bool Executor::_invoke_runtime_task(
Worker& worker, Node* node) {
779 return _invoke_runtime_task_impl(
780 worker, node, std::get_if<Node::Runtime>(&node->_handle)->work
785inline bool Executor::_invoke_runtime_task_impl(
786 Worker& worker, Node* node, std::function<
void(Runtime&)>& work
789 if((node->_nstate & NSTATE::PREEMPTED) == 0) {
791 Runtime rt(*
this, worker, node);
793 node->_nstate |= (NSTATE::PREEMPTED | NSTATE::IMPLICITLY_ANCHORED);
795 node->_join_counter.fetch_add(1, std::memory_order_release);
797 _observer_prologue(worker, node);
798 TF_EXECUTOR_EXCEPTION_HANDLER(worker, node, {
801 _observer_epilogue(worker, node);
804 if(node->_join_counter.fetch_sub(1, std::memory_order_acq_rel) == 1) {
805 node->_nstate &= ~(NSTATE::PREEMPTED | NSTATE::IMPLICITLY_ANCHORED);
819 node->_nstate &= ~(NSTATE::PREEMPTED | NSTATE::IMPLICITLY_ANCHORED);
825inline bool Executor::_invoke_runtime_task_impl(
826 Worker& worker, Node* node, std::function<
void(Runtime&,
bool)>& work
829 Runtime rt(*
this, worker, node);
832 if((node->_nstate & NSTATE::PREEMPTED) == 0) {
834 node->_nstate |= (NSTATE::PREEMPTED | NSTATE::IMPLICITLY_ANCHORED);
835 node->_join_counter.fetch_add(1, std::memory_order_release);
837 _observer_prologue(worker, node);
838 TF_EXECUTOR_EXCEPTION_HANDLER(worker, node, {
841 _observer_epilogue(worker, node);
844 if(node->_join_counter.fetch_sub(1, std::memory_order_acq_rel) == 1) {
845 node->_nstate &= ~(NSTATE::PREEMPTED | NSTATE::IMPLICITLY_ANCHORED);
858 node->_nstate &= ~(NSTATE::PREEMPTED | NSTATE::IMPLICITLY_ANCHORED);
874class NonpreemptiveRuntime {
876 friend class Executor;
883 void schedule(Task task);
890 explicit NonpreemptiveRuntime(Executor& executor, Worker& worker) :
891 _executor {executor}, _worker {worker}{
906inline void NonpreemptiveRuntime::schedule(Task task) {
908 auto node = task._node;
912 node->_join_counter.store(0, std::memory_order_relaxed);
914 auto& j = node->_parent ? node->_parent->_join_counter :
915 node->_topology->_join_counter;
916 j.fetch_add(1, std::memory_order_relaxed);
917 _executor._schedule(_worker, node);
925inline void Executor::_invoke_nonpreemptive_runtime_task(Worker& worker, Node* node) {
926 _observer_prologue(worker, node);
927 tf::NonpreemptiveRuntime nprt(*
this, worker);
928 TF_EXECUTOR_EXCEPTION_HANDLER(worker, node, {
929 std::get_if<Node::NonpreemptiveRuntime>(&node->_handle)->work(nprt);
931 _observer_epilogue(worker, node);
936template <
typename P,
typename C>
940 if(f.empty() || p()) {
942 std::promise<void> promise;
947 _increment_topology();
949 auto g = std::make_unique<Taskflow>(std::move(f));
952 auto t = std::make_shared<Topology>(*g, std::forward<P>(p), std::forward<C>(c));
960 t->_parent = rt._node;
961 t->_parent->_join_counter.fetch_add(1, std::memory_order_release);
962 if(g.object->_fetch_enqueue(t) == 0) {
963 rt._executor._schedule_graph(
964 rt._worker, g.object->_graph, t.get(), t.get()
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:191
class to create an executor
Definition executor.hpp:62
void silent_async(P &¶ms, F &&func)
similar to tf::Executor::async but does not return a future object
tf::Future< void > run_until(Taskflow &taskflow, P &&pred)
runs a taskflow multiple times until the predicate becomes true
class to build a task dependency graph
Definition flow_builder.hpp:114
class to access the result of an execution
Definition taskflow.hpp:630
class to create a runtime task
Definition runtime.hpp:47
void silent_async(F &&f)
runs the given function asynchronously without returning any future object
Definition runtime.hpp:667
bool is_cancelled()
queries if this runtime task has been cancelled
Definition runtime.hpp:657
Executor & executor()
obtains the running executor
Definition runtime.hpp:617
auto async(F &&f)
runs the given callable asynchronously
Definition runtime.hpp:686
tf::AsyncTask silent_dependent_async(F &&func, Tasks &&... tasks)
runs the given function asynchronously when the given predecessors finish
Definition runtime.hpp:705
void schedule(Task task)
schedules an active task immediately to the worker's queue
Definition runtime.hpp:627
void corun()
corun all tasks spawned by this runtime with other workers
Definition runtime.hpp:642
auto dependent_async(F &&func, Tasks &&... tasks)
runs the given function asynchronously when the given predecessors finish
Definition runtime.hpp:745
Worker & worker()
acquire a reference to the underlying worker
Definition runtime.hpp:622
void corun_all()
equivalent to tf::Runtime::corun - just an alias for legacy purpose
Definition runtime.hpp:653
class to create a task handle over a taskflow node
Definition task.hpp:569
class to create a taskflow object
Definition taskflow.hpp:64
class to create a worker in an executor
Definition worker.hpp:55
concept to check if a type is a tf::AsyncTask
Definition async_task.hpp:314
concept that determines if a type is a task parameter type
Definition graph.hpp:202
taskflow namespace
Definition small_vector.hpp:20