| Taskflow: A General-purpose Task-parallel Programming System

Loading...
Searching...
No Matches
Public Member Functions | Friends | List of all members
tf::Executor Class Reference

class to create an executor More...

`#include <taskflow/core/executor.hpp>`

## Public Member Functions
| Executor (size_t N=std::thread::hardware_concurrency(), std::shared_ptr< WorkerInterface > wif=nullptr)
| constructs the executor with `N` worker threads
| ~Executor ()
| destructs the executor
tf::Future< void > | run (Taskflow &taskflow)
| runs a taskflow once
tf::Future< void > | run (Taskflow &&taskflow)
| runs a moved taskflow once
template<typename C >
tf::Future< void > | run (Taskflow &taskflow, C &&callable)
| runs a taskflow once and invoke a callback upon completion
template<typename C >
tf::Future< void > | run (Taskflow &&taskflow, C &&callable)
| runs a moved taskflow once and invoke a callback upon completion
tf::Future< void > | run_n (Taskflow &taskflow, size_t N)
| runs a taskflow for `N` times
tf::Future< void > | run_n (Taskflow &&taskflow, size_t N)
| runs a moved taskflow for `N` times
template<typename C >
tf::Future< void > | run_n (Taskflow &taskflow, size_t N, C &&callable)
| runs a taskflow for `N` times and then invokes a callback
template<typename C >
tf::Future< void > | run_n (Taskflow &&taskflow, size_t N, C &&callable)
| runs a moved taskflow for `N` times and then invokes a callback
template<typename P >
tf::Future< void > | run_until (Taskflow &taskflow, P &&pred)
| runs a taskflow multiple times until the predicate becomes true
template<typename P >
tf::Future< void > | run_until (Taskflow &&taskflow, P &&pred)
| runs a moved taskflow and keeps running it until the predicate becomes true
template<typename P , typename C >
tf::Future< void > | run_until (Taskflow &taskflow, P &&pred, C &&callable)
| runs a taskflow multiple times until the predicate becomes true and then invokes the callback
template<typename P , typename C >
tf::Future< void > | run_until (Taskflow &&taskflow, P &&pred, C &&callable)
| runs a moved taskflow and keeps running it until the predicate becomes true and then invokes the callback
template<typename T >
void | corun (T &target)
| runs a target graph and waits until it completes using an internal worker of this executor
template<typename P >
void | corun_until (P &&predicate)
| keeps running the work-stealing loop until the predicate returns `true`
void | wait_for_all ()
| waits for all tasks to complete
size_t | num_workers () const noexcept
| queries the number of worker threads
size_t | num_waiters () const noexcept
| queries the number of workers that are in the waiting loop
size_t | num_queues () const noexcept
| queries the number of work-stealing queues used by the executor
size_t | num_topologies () const
| queries the number of running topologies at the time of this call
Worker * | this_worker ()
| queries pointer to the calling worker if it belongs to this executor, otherwise returns `nullptr`
int | this_worker_id () const
| queries the id of the caller thread within this executor
template<typename Observer , typename... ArgsT>
std::shared_ptr< Observer > | make_observer (ArgsT &&... args)
| constructs an observer to inspect the activities of worker threads
template<typename Observer >
void | remove_observer (std::shared_ptr< Observer > observer)
| removes an observer from the executor
size_t | num_observers () const noexcept
| queries the number of observers
template<TaskParamsLike P, typename F >
auto | async (P &&params, F &&func)
| creates a parameterized asynchronous task to run the given function
template<typename F >
auto | async (F &&func)
| runs a given function asynchronously
template<TaskParamsLike P, typename F >
void | silent_async (P &&params, F &&func)
| similar to tf::Executor::async but does not return a future object
template<typename F >
void | silent_async (F &&func)
| similar to tf::Executor::async but does not return a future object
template<typename F , AsyncTaskHandleLike... Tasks>
tf::AsyncTask | silent_dependent_async (F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<TaskParamsLike P, typename F , AsyncTaskHandleLike... Tasks>
tf::AsyncTask | silent_dependent_async (P &&params, F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<typename F , std::input_iterator I>
tf::AsyncTask | silent_dependent_async (F &&func, I first, I last)
| runs the given function asynchronously when the given range of predecessors finish
template<TaskParamsLike P, typename F , std::input_iterator I>
tf::AsyncTask | silent_dependent_async (P &&params, F &&func, I first, I last)
| runs the given function asynchronously when the given range of predecessors finish
template<typename F , AsyncTaskHandleLike... Tasks>
auto | dependent_async (F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<TaskParamsLike P, typename F , AsyncTaskHandleLike... Tasks>
auto | dependent_async (P &&params, F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<typename F , std::input_iterator I>
auto | dependent_async (F &&func, I first, I last)
| runs the given function asynchronously when the given range of predecessors finish
template<TaskParamsLike P, typename F , std::input_iterator I>
auto | dependent_async (P &&params, F &&func, I first, I last)
| runs the given function asynchronously when the given range of predecessors finish
TaskGroup | task_group ()
| creates a task group that executes a collection of asynchronous tasks

## Friends
class | FlowBuilder
class | Subflow
class | Runtime
class | NonpreemptiveRuntime
class | Algorithm
class | TaskGroup

## Detailed Description

class to create an executor

An tf::Executor manages a set of worker threads to run tasks using an efficient work-stealing scheduling algorithm.

// Declare an executor and a taskflow
tf::Executor executor;
tf::Taskflow taskflow;
// Add three tasks into the taskflow
tf::Task A = taskflow.emplace([] () { std::cout << "This is TaskA\n"; });
tf::Task B = taskflow.emplace([] () { std::cout << "This is TaskB\n"; });
tf::Task C = taskflow.emplace([] () { std::cout << "This is TaskC\n"; });
// Build precedence between tasks
A.precede(B, C);
tf::Future<void> fu = executor.run(taskflow);
fu.wait(); // block until the execution completes
executor.run(taskflow, [](){ std::cout << "end of 1 run"; }).wait();
executor.run_n(taskflow, 4);
executor.wait_for_all(); // block until all associated executions finish
executor.run_n(taskflow, 4, [](){ std::cout << "end of 4 runs"; }).wait();
executor.run_until(taskflow, [cnt=0] () mutable { return ++cnt == 10; });
tf::Executor
class to create an executor
Definition executor.hpp:62
tf::Executor::run_until
tf::Future< void > run_until(Taskflow &taskflow, P &&pred)
runs a taskflow multiple times until the predicate becomes true
tf::Executor::run
tf::Future< void > run(Taskflow &taskflow)
runs a taskflow once
tf::Executor::run_n
tf::Future< void > run_n(Taskflow &taskflow, size_t N)
runs a taskflow for N times
tf::Executor::wait_for_all
void wait_for_all()
waits for all tasks to complete
tf::FlowBuilder::emplace
Task emplace(C &&callable)
creates a static task
Definition flow_builder.hpp:1738
tf::Future
class to access the result of an execution
Definition taskflow.hpp:630
tf::Task
class to create a task handle over a taskflow node
Definition task.hpp:569
tf::Task::precede
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition task.hpp:1258
tf::Taskflow
class to create a taskflow object
Definition taskflow.hpp:64

Most executor methods are thread-safe. For example, you can submit multiple taskflows to an executor concurrently from different threads, while other threads simultaneously create asynchronous tasks.

std::thread t1([&](){ executor.run(taskflow); };
std::thread t2([&](){ executor.async([](){ std::cout << "async task from t2\n"; }); });
executor.async([&](){ std::cout << "async task from the main thread\n"; });
tf::Executor::async
auto async(P &&params, F &&func)
creates a parameterized asynchronous task to run the given function

Note
  To know more about tf::Executor, please refer to Executor.

## Constructor & Destructor Documentation

## ◆ Executor()

tf::Executor::Executor | ( | size_t | N = std::thread::hardware_concurrency(),
|  | std::shared_ptr< WorkerInterface > | wif = nullptr )
| explicit

constructs the executor with `N` worker threads

Parameters

  N | number of workers (default std::thread::hardware_concurrency)
  wif | interface class instance to configure workers' behaviors

The constructor spawns `N` worker threads to run tasks in a work-stealing loop. The number of workers must be greater than zero or an exception will be thrown. By default, the number of worker threads is equal to the maximum hardware concurrency returned by std::thread::hardware_concurrency.

Users can alter the worker behavior, such as changing thread affinity, via deriving an instance from tf::WorkerInterface.

Note
  An exception will be thrown if executor construction fails.

## ◆ ~Executor()

tf::Executor::~Executor | ( |  | ) |

destructs the executor

The destructor calls Executor::wait_for_all to wait for all submitted taskflows to complete and then notifies all worker threads to stop and join these threads.

## Member Function Documentation

## ◆ async() [1/2]

template<typename F >

auto tf::Executor::async | ( | F && | func | ) |

runs a given function asynchronously

Template Parameters

  F | callable type

Parameters

  func | callable object

Returns
  a std::future that will hold the result of the execution

The method creates an asynchronous task to run the given function and return a std::future object that eventually will hold the result of the return value.

std::future<int> future = executor.async([](){
std::cout << "create an asynchronous task and returns 1\n";
return 1;
});
future.get();

This member function is thread-safe.

## ◆ async() [2/2]

template<TaskParamsLike P, typename F >

auto tf::Executor::async | ( | P && | params,
|  | F && | func )

creates a parameterized asynchronous task to run the given function

Template Parameters

  P | task parameter type satisfying tf::TaskParamsLike
  F | callable type

Parameters

  params | task parameters
  func | callable object

Returns
  a std::future that will hold the result of the execution

The method creates a parameterized asynchronous task to run the given function and return a std::future object that eventually will hold the result of the execution.

std::future<int> future = executor.async("name", [](){
std::cout << "create an asynchronous task with a name and returns 1\n";
return 1;
});
future.get();

This member function is thread-safe.

## ◆ corun()

template<typename T >

void tf::Executor::corun | ( | T & | target | ) |

runs a target graph and waits until it completes using an internal worker of this executor

Template Parameters

  T | target type which is either convertible to `tf::Graph` or has `tf::Graph& T::graph()` defined

Parameters

  target | the target task graph object

The method coruns a target graph cooperatively with other workers in the same executor and block until the execution completes. Under cooperative execution, a worker is not preempted. Instead, it continues participating in the work-stealing loop, executing available tasks alongside other workers.

tf::Executor executor(2);
tf::Taskflow taskflow;
std::array<tf::Taskflow, 1000> others;
std::atomic<size_t> counter{0};
for(size_t n=0; n<1000; n++) {
for(size_t i=0; i<1000; i++) {
others[n].emplace([&](){ counter++; });
}
taskflow.emplace([&executor, &tf=others[n]](){
executor.corun(tf);
//executor.run(tf).wait(); <- blocking the worker without doing anything
// will introduce deadlock
});
}
executor.run(taskflow).wait();
tf
taskflow namespace
Definition small_vector.hpp:20

The method is thread-safe as long as the target is not concurrently ran by two or more threads.

Note
  You must call tf::Executor::corun from a worker of the calling executor or an exception will be thrown.

## ◆ corun_until()

template<typename P >

void tf::Executor::corun_until | ( | P && | predicate | ) |

keeps running the work-stealing loop until the predicate returns `true`

Template Parameters

  P | predicate type

Parameters

  predicate | a boolean predicate to indicate when to stop the loop

The method keeps the caller worker running in the work-stealing loop until the stop predicate becomes true.

The method keeps the calling worker running available tasks cooperatively with other workers in the same executor and block until the predicate return `true`. Under cooperative execution, a worker is not preempted. Instead, it continues participating in the work-stealing loop, executing available tasks alongside other workers.

taskflow.emplace([&](){
std::future<void> fu = std::async([](){ std::sleep(100s); });
executor.corun_until([](){
return fu.wait_for(std::chrono::seconds(0)) == future_status::ready;
});
});

Note
  You must call tf::Executor::corun_until from a worker of the calling executor or an exception will be thrown.

## ◆ dependent_async() [1/4]

template<typename F , std::input_iterator I>

auto tf::Executor::dependent_async | ( | F && | func,
|  | I | first,
|  | I | last )

runs the given function asynchronously when the given range of predecessors finish

Template Parameters

  F | callable type
  I | iterator type

Parameters

  func | callable object
  first | iterator to the beginning (inclusive)
  last | iterator to the end (exclusive)

Returns
  a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution

The example below creates three asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`. Task `C` returns a pair of its tf::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution.

std::array<tf::AsyncTask, 2> array {
executor.silent_dependent_async([](){ printf("A\n"); }),
executor.silent_dependent_async([](){ printf("B\n"); })
};
auto [C, fuC] = executor.dependent_async(
[](){
printf("C runs after A and B\n");
return 1;
},
array.begin(), array.end()
);
assert(fuC.get()==1); // C finishes, which in turns means both A and B finish

You can mix the use of tf::AsyncTask handles returned by tf::Executor::dependent_async and tf::Executor::silent_dependent_async when specifying task dependencies.

This member function is thread-safe.

## ◆ dependent_async() [2/4]

template<typename F , AsyncTaskHandleLike... Tasks>

auto tf::Executor::dependent_async | ( | F && | func,
|  | Tasks &&... | tasks )

runs the given function asynchronously when the given predecessors finish

Template Parameters

  F | callable type
  Tasks | task types convertible to tf::AsyncTask

Parameters

  func | callable object
  tasks | asynchronous tasks on which this execution depends

Returns
  a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution

The example below creates three asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`. Task `C` returns a pair of its tf::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution.

tf::AsyncTask A = executor.silent_dependent_async([](){ printf("A\n"); });
tf::AsyncTask B = executor.silent_dependent_async([](){ printf("B\n"); });
auto [C, fuC] = executor.dependent_async(
[](){
printf("C runs after A and B\n");
return 1;
},
A, B
);
fuC.get(); // C finishes, which in turns means both A and B finish
tf::AsyncTask
class to hold a dependent asynchronous task with shared ownership
Definition async_task.hpp:45

You can mix the use of tf::AsyncTask handles returned by tf::Executor::dependent_async and tf::Executor::silent_dependent_async when specifying task dependencies.

This member function is thread-safe.

## ◆ dependent_async() [3/4]

template<TaskParamsLike P, typename F , std::input_iterator I>

auto tf::Executor::dependent_async | ( | P && | params,
|  | F && | func,
|  | I | first,
|  | I | last )

runs the given function asynchronously when the given range of predecessors finish

Template Parameters

  P | task parameters type satisfying tf::TaskParamsLike
  F | callable type
  I | iterator type

Parameters

  params | task parameters
  func | callable object
  first | iterator to the beginning (inclusive)
  last | iterator to the end (exclusive)

Returns
  a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution

The example below creates three named asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`. Task `C` returns a pair of its tf::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution. Assigned task names will appear in the observers of the executor.

std::array<tf::AsyncTask, 2> array {
executor.silent_dependent_async("A", [](){ printf("A\n"); }),
executor.silent_dependent_async("B", [](){ printf("B\n"); })
};
auto [C, fuC] = executor.dependent_async(
"C",
[](){
printf("C runs after A and B\n");
return 1;
},
array.begin(), array.end()
);
assert(fuC.get()==1); // C finishes, which in turns means both A and B finish

You can mix the use of tf::AsyncTask handles returned by tf::Executor::dependent_async and tf::Executor::silent_dependent_async when specifying task dependencies.

This member function is thread-safe.

## ◆ dependent_async() [4/4]

template<TaskParamsLike P, typename F , AsyncTaskHandleLike... Tasks>

auto tf::Executor::dependent_async | ( | P && | params,
|  | F && | func,
|  | Tasks &&... | tasks )

runs the given function asynchronously when the given predecessors finish

Template Parameters

  P | task parameters type satisfying tf::TaskParamsLike
  F | callable type
  Tasks | task types convertible to tf::AsyncTask

Parameters

  params | task parameters
  func | callable object
  tasks | asynchronous tasks on which this execution depends

Returns
  a pair of a tf::AsyncTask handle and a std::future that holds the result of the execution

The example below creates three named asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`. Task `C` returns a pair of its tf::AsyncTask handle and a std::future<int> that eventually will hold the result of the execution. Assigned task names will appear in the observers of the executor.

tf::AsyncTask A = executor.silent_dependent_async("A", [](){ printf("A\n"); });
tf::AsyncTask B = executor.silent_dependent_async("B", [](){ printf("B\n"); });
auto [C, fuC] = executor.dependent_async(
"C",
[](){
printf("C runs after A and B\n");
return 1;
},
A, B
);
assert(fuC.get()==1); // C finishes, which in turns means both A and B finish

You can mix the use of tf::AsyncTask handles returned by tf::Executor::dependent_async and tf::Executor::silent_dependent_async when specifying task dependencies.

This member function is thread-safe.

## ◆ make_observer()

template<typename Observer , typename... ArgsT>

std::shared_ptr< Observer > tf::Executor::make_observer | ( | ArgsT &&... | args | ) |

constructs an observer to inspect the activities of worker threads

Template Parameters

  Observer | observer type derived from tf::ObserverInterface
  ArgsT | argument parameter pack

Parameters

  args | arguments to forward to the constructor of the observer

Returns
  a shared pointer to the created observer

Each executor manages a list of observers with shared ownership with callers. For each of these observers, the two member functions, tf::ObserverInterface::on_entry and tf::ObserverInterface::on_exit will be called before and after the execution of a task.

This member function is not thread-safe.

## ◆ num_topologies()

size_t tf::Executor::num_topologies | ( |  | ) | const

queries the number of running topologies at the time of this call

When a taskflow is submitted to an executor, a topology is created to store runtime metadata of the running taskflow. When the execution of the submitted taskflow finishes, its corresponding topology will be removed from the executor.

executor.run(taskflow);
std::cout << executor.num_topologies(); // 0 or 1 (taskflow still running)

## ◆ num_waiters()

size_t tf::Executor::num_waiters | ( |  | ) | const
| noexcept

queries the number of workers that are in the waiting loop

A worker in the waiting loop has exhausted its local queue and made enough stealing attempts, and is now ready to be preempted and enter the waiting state.

## ◆ num_workers()

size_t tf::Executor::num_workers | ( |  | ) | const
| noexcept

queries the number of worker threads

Each worker represents a unique thread spawned by an executor upon its construction time.

tf::Executor executor(4);
std::cout << executor.num_workers(); // 4

## ◆ remove_observer()

template<typename Observer >

void tf::Executor::remove_observer | ( | std::shared_ptr< Observer > | observer | ) |

removes an observer from the executor

This member function is not thread-safe.

## ◆ run() [1/4]

tf::Future< void > tf::Executor::run | ( | Taskflow && | taskflow | ) |

runs a moved taskflow once

Parameters

  taskflow | a moved tf::Taskflow object

Returns
  a tf::Future that holds the result of the execution

This member function executes a moved taskflow once and returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow.

tf::Future<void> future = executor.run(std::move(taskflow));
// do something else
future.wait();

This member function is thread-safe.

## ◆ run() [2/4]

template<typename C >

tf::Future< void > tf::Executor::run | ( | Taskflow && | taskflow,
|  | C && | callable )

runs a moved taskflow once and invoke a callback upon completion

Parameters

  taskflow | a moved tf::Taskflow object
  callable | a callable object to be invoked after this run

Returns
  a tf::Future that holds the result of the execution

This member function executes a moved taskflow once and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow.

tf::Future<void> future = executor.run(
std::move(taskflow), [](){ std::cout << "done"; }
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run() [3/4]

tf::Future< void > tf::Executor::run | ( | Taskflow & | taskflow | ) |

runs a taskflow once

Parameters

  taskflow | a tf::Taskflow object

Returns
  a tf::Future that holds the result of the execution

This member function executes the given taskflow once and returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run(taskflow);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run() [4/4]

template<typename C >

tf::Future< void > tf::Executor::run | ( | Taskflow & | taskflow,
|  | C && | callable )

runs a taskflow once and invoke a callback upon completion

Parameters

  taskflow | a tf::Taskflow object
  callable | a callable object to be invoked after this run

Returns
  a tf::Future that holds the result of the execution

This member function executes the given taskflow once and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run(taskflow, [](){ std::cout << "done"; });
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_n() [1/4]

tf::Future< void > tf::Executor::run_n | ( | Taskflow && | taskflow,
|  | size_t | N )

runs a moved taskflow for `N` times

Parameters

  taskflow | a moved tf::Taskflow object
  N | number of runs

Returns
  a tf::Future that holds the result of the execution

This member function executes a moved taskflow `N` times and returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow.

tf::Future<void> future = executor.run_n(
std::move(taskflow), 2 // run the moved taskflow 2 times
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_n() [2/4]

template<typename C >

tf::Future< void > tf::Executor::run_n | ( | Taskflow && | taskflow,
|  | size_t | N,
|  | C && | callable )

runs a moved taskflow for `N` times and then invokes a callback

Parameters

  taskflow | a moved tf::Taskflow
  N | number of runs
  callable | a callable object to be invoked after this run

Returns
  a tf::Future that holds the result of the execution

This member function executes a moved taskflow `N` times and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run_n(
// run the moved taskflow 2 times and invoke the lambda to print "done"
std::move(taskflow), 2, [](){ std::cout << "done"; }
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_n() [3/4]

tf::Future< void > tf::Executor::run_n | ( | Taskflow & | taskflow,
|  | size_t | N )

runs a taskflow for `N` times

Parameters

  taskflow | a tf::Taskflow object
  N | number of runs

Returns
  a tf::Future that holds the result of the execution

This member function executes the given taskflow `N` times and returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run_n(taskflow, 2); // run taskflow 2 times
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_n() [4/4]

template<typename C >

tf::Future< void > tf::Executor::run_n | ( | Taskflow & | taskflow,
|  | size_t | N,
|  | C && | callable )

runs a taskflow for `N` times and then invokes a callback

Parameters

  taskflow | a tf::Taskflow
  N | number of runs
  callable | a callable object to be invoked after this run

Returns
  a tf::Future that holds the result of the execution

This member function executes the given taskflow `N` times and invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run(
taskflow, 2, [](){ std::cout << "done"; } // runs taskflow 2 times and invoke
// the lambda to print "done"
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_until() [1/4]

template<typename P >

tf::Future< void > tf::Executor::run_until | ( | Taskflow && | taskflow,
|  | P && | pred )

runs a moved taskflow and keeps running it until the predicate becomes true

Parameters

  taskflow | a moved tf::Taskflow object
  pred | a boolean predicate to return `true` for stop

Returns
  a tf::Future that holds the result of the execution

This member function executes a moved taskflow multiple times until the predicate returns `true`. This member function returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow.

tf::Future<void> future = executor.run_until(
std::move(taskflow), [](){ return rand()%10 == 0 }
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_until() [2/4]

template<typename P , typename C >

tf::Future< void > tf::Executor::run_until | ( | Taskflow && | taskflow,
|  | P && | pred,
|  | C && | callable )

runs a moved taskflow and keeps running it until the predicate becomes true and then invokes the callback

Parameters

  taskflow | a moved tf::Taskflow
  pred | a boolean predicate to return `true` for stop
  callable | a callable object to be invoked after this run completes

Returns
  a tf::Future that holds the result of the execution

This member function executes a moved taskflow multiple times until the predicate returns `true` and then invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution. The executor will take care of the lifetime of the moved taskflow.

tf::Future<void> future = executor.run_until(
std::move(taskflow),
[](){ return rand()%10 == 0 }, [](){ std::cout << "done"; }
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_until() [3/4]

template<typename P >

tf::Future< void > tf::Executor::run_until | ( | Taskflow & | taskflow,
|  | P && | pred )

runs a taskflow multiple times until the predicate becomes true

Parameters

  taskflow | a tf::Taskflow
  pred | a boolean predicate to return `true` for stop

Returns
  a tf::Future that holds the result of the execution

This member function executes the given taskflow multiple times until the predicate returns `true`. This member function returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run_until(
taskflow, [](){ return rand()%10 == 0 }
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ run_until() [4/4]

template<typename P , typename C >

tf::Future< void > tf::Executor::run_until | ( | Taskflow & | taskflow,
|  | P && | pred,
|  | C && | callable )

runs a taskflow multiple times until the predicate becomes true and then invokes the callback

Parameters

  taskflow | a tf::Taskflow
  pred | a boolean predicate to return `true` for stop
  callable | a callable object to be invoked after this run completes

Returns
  a tf::Future that holds the result of the execution

This member function executes the given taskflow multiple times until the predicate returns `true` and then invokes the given callable when the execution completes. This member function returns a tf::Future object that eventually holds the result of the execution.

tf::Future<void> future = executor.run_until(
taskflow, [](){ return rand()%10 == 0 }, [](){ std::cout << "done"; }
);
// do something else
future.wait();

This member function is thread-safe.

## ◆ silent_async() [1/2]

template<typename F >

void tf::Executor::silent_async | ( | F && | func | ) |

similar to tf::Executor::async but does not return a future object

Template Parameters

  F | callable type

Parameters

  func | callable object

The method creates an asynchronous task to run the given function without returning any std::future object. This member function is more efficient than tf::Executor::async and is encouraged to use when applications do not need a std::future to acquire the result or synchronize the execution.

executor.silent_async([](){
std::cout << "create an asynchronous task with no return\n";
});
executor.wait_for_all();

This member function is thread-safe.

## ◆ silent_async() [2/2]

template<TaskParamsLike P, typename F >

void tf::Executor::silent_async | ( | P && | params,
|  | F && | func )

similar to tf::Executor::async but does not return a future object

Template Parameters

  P | task parameter type satisfying tf::TaskParamsLike
  F | callable type

Parameters

  params | task parameters
  func | callable object

The method creates a parameterized asynchronous task to run the given function without returning any std::future object. This member function is more efficient than tf::Executor::async and is encouraged to use when applications do not need a std::future to acquire the result or synchronize the execution.

executor.silent_async("name", [](){
std::cout << "create an asynchronous task with a name and no return\n";
});
executor.wait_for_all();

This member function is thread-safe.

## ◆ silent_dependent_async() [1/4]

template<typename F , std::input_iterator I>

tf::AsyncTask tf::Executor::silent_dependent_async | ( | F && | func,
|  | I | first,
|  | I | last )

runs the given function asynchronously when the given range of predecessors finish

Template Parameters

  F | callable type
  I | iterator type

Parameters

  func | callable object
  first | iterator to the beginning (inclusive)
  last | iterator to the end (exclusive)

Returns
  a tf::AsyncTask handle

This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`.

std::array<tf::AsyncTask, 2> array {
executor.silent_dependent_async([](){ printf("A\n"); }),
executor.silent_dependent_async([](){ printf("B\n"); })
};
executor.silent_dependent_async(
[](){ printf("C runs after A and B\n"); }, array.begin(), array.end()
);
executor.wait_for_all();

This member function is thread-safe.

## ◆ silent_dependent_async() [2/4]

template<typename F , AsyncTaskHandleLike... Tasks>

tf::AsyncTask tf::Executor::silent_dependent_async | ( | F && | func,
|  | Tasks &&... | tasks )

runs the given function asynchronously when the given predecessors finish

Template Parameters

  F | callable type
  Tasks | task types convertible to tf::AsyncTask

Parameters

  func | callable object
  tasks | asynchronous tasks on which this execution depends

Returns
  a tf::AsyncTask handle

This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`.

tf::AsyncTask A = executor.silent_dependent_async([](){ printf("A\n"); });
tf::AsyncTask B = executor.silent_dependent_async([](){ printf("B\n"); });
executor.silent_dependent_async([](){ printf("C runs after A and B\n"); }, A, B);
executor.wait_for_all();

This member function is thread-safe.

## ◆ silent_dependent_async() [3/4]

template<TaskParamsLike P, typename F , std::input_iterator I>

tf::AsyncTask tf::Executor::silent_dependent_async | ( | P && | params,
|  | F && | func,
|  | I | first,
|  | I | last )

runs the given function asynchronously when the given range of predecessors finish

Template Parameters

  P | task parameter type satisfying tf::TaskParamsLike
  F | callable type
  I | iterator type

Parameters

  params | tasks parameters
  func | callable object
  first | iterator to the beginning (inclusive)
  last | iterator to the end (exclusive)

Returns
  a tf::AsyncTask handle

This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`. Assigned task names will appear in the observers of the executor.

std::array<tf::AsyncTask, 2> array {
executor.silent_dependent_async("A", [](){ printf("A\n"); }),
executor.silent_dependent_async("B", [](){ printf("B\n"); })
};
executor.silent_dependent_async(
"C", [](){ printf("C runs after A and B\n"); }, array.begin(), array.end()
);
executor.wait_for_all();

This member function is thread-safe.

## ◆ silent_dependent_async() [4/4]

template<TaskParamsLike P, typename F , AsyncTaskHandleLike... Tasks>

tf::AsyncTask tf::Executor::silent_dependent_async | ( | P && | params,
|  | F && | func,
|  | Tasks &&... | tasks )

runs the given function asynchronously when the given predecessors finish

Template Parameters

  P | task parameter type satisfying tf::TaskParamsLike
  F | callable type
  Tasks | task types convertible to tf::AsyncTask

Parameters

  params | task parameters
  func | callable object
  tasks | asynchronous tasks on which this execution depends

Returns
  a tf::AsyncTask handle

This member function is more efficient than tf::Executor::dependent_async and is encouraged to use when you do not want a std::future to acquire the result or synchronize the execution. The example below creates three asynchronous tasks, `A`, `B`, and `C`, in which task `C` runs after task `A` and task `B`. Assigned task names will appear in the observers of the executor.

tf::AsyncTask A = executor.silent_dependent_async("A", [](){ printf("A\n"); });
tf::AsyncTask B = executor.silent_dependent_async("B", [](){ printf("B\n"); });
executor.silent_dependent_async(
"C", [](){ printf("C runs after A and B\n"); }, A, B
);
executor.wait_for_all();

This member function is thread-safe.

## ◆ task_group()

TaskGroup tf::Executor::task_group | ( |  | ) |
| inline

creates a task group that executes a collection of asynchronous tasks

Returns
  a tf::TaskGroup object associated with the current executor

A TaskGroup allows submitting multiple asynchronous tasks to the executor and waiting for their completion collectively using `corun()`. Tasks added to the group can execute in parallel and may capture local variables by value or reference, depending on your needs. This can be useful for divide-and-conquer algorithms, parallel loops, or any workflow that requires grouping related tasks.

Example (computing Fibonacci numbers in parallel):

tf::Executor executor;
size_t fibonacci(size_t N) {
if (N < 2) return N;
size_t res1, res2;
// Create a task group from the current executor
tf::TaskGroup tg = get_executor().task_group();
// Submit asynchronous tasks to the group
tg.silent_async([N, &res1](){ res1 = fibonacci(N-1); });
res2 = fibonacci(N-2); // compute one branch synchronously
// Wait for all tasks in the group to complete
tg.corun();
return res1 + res2;
}
int main() {
return executor.async([](){ return fibonacci(30); }).get();
}
tf::TaskGroup
class to create a task group from a task
Definition task_group.hpp:61
tf::TaskGroup::corun
void corun()
corun all tasks spawned by this task group with other workers
Definition task_group.hpp:721
tf::TaskGroup::silent_async
void silent_async(F &&f)
runs the given function asynchronously without returning any future object
Definition task_group.hpp:752

This member function is thread-safe.

Note
  Due to cooperative execution, a task group can only be created by a worker of an executor.

## ◆ this_worker()

Worker * tf::Executor::this_worker | ( |  | ) |

queries pointer to the calling worker if it belongs to this executor, otherwise returns `nullptr`

Returns a pointer to the per-worker storage associated with this executor. If the calling thread is not a worker of this executor, the function returns `nullptr`.

auto w = executor.this_worker();
tf::Taskflow taskflow;
tf::Executor executor;
executor.async([&](){
assert(executor.this_worker() != nullptr);
assert(executor.this_worker()->executor() == &executor);
});
tf::Executor::this_worker
Worker * this_worker()
queries pointer to the calling worker if it belongs to this executor, otherwise returns nullptr

## ◆ this_worker_id()

int tf::Executor::this_worker_id | ( |  | ) | const

queries the id of the caller thread within this executor

Each worker has an unique id in the range of `0` to `N-1` associated with its parent executor. If the caller thread does not belong to the executor, `-1` is returned.

tf::Executor executor(4); // 4 workers in the executor
executor.this_worker_id(); // -1 (main thread is not a worker)
taskflow.emplace([&](){
std::cout << executor.this_worker_id(); // 0, 1, 2, or 3
});
executor.run(taskflow);

## ◆ wait_for_all()

void tf::Executor::wait_for_all | ( |  | ) |

waits for all tasks to complete

This member function waits until all submitted tasks (e.g., taskflows, asynchronous tasks) to finish.

executor.run(taskflow1);
executor.run_n(taskflow2, 10);
executor.run_n(taskflow3, 100);
executor.wait_for_all(); // wait until the above submitted taskflows finish

The documentation for this class was generated from the following files:

- taskflow/core/executor.hpp
- taskflow/core/runtime.hpp
- taskflow/core/task_group.hpp