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

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

class to create a runtime task More...

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

## Public Member Functions
Executor & | executor ()
| obtains the running executor
Worker & | worker ()
| acquire a reference to the underlying worker
void | schedule (Task task)
| schedules an active task immediately to the worker's queue
template<typename F>
auto | async (F &&f)
| runs the given callable asynchronously
template<typename P, typename F>
auto | async (P &&params, F &&f)
| runs the given callable asynchronously
template<typename F>
void | silent_async (F &&f)
| runs the given function asynchronously without returning any future object
template<typename P, typename F>
void | silent_async (P &&params, F &&f)
| runs the given function asynchronously without returning any future object
template<typename F, typename... Tasks> requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)
auto | dependent_async (F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<TaskParamsConcept P, typename F, typename... Tasks> requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)
auto | dependent_async (P &&params, F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<typename F, typename I> requires (!std::same_as<std::decay_t<I>, AsyncTask>)
auto | dependent_async (F &&func, I first, I last)
| runs the given function asynchronously when the given range of predecessors finish
template<TaskParamsConcept P, typename F, typename I> requires (!std::same_as<std::decay_t<I>, AsyncTask>)
auto | 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, typename... Tasks> requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)
tf::AsyncTask | silent_dependent_async (F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<TaskParamsConcept P, typename F, typename... Tasks> requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)
tf::AsyncTask | silent_dependent_async (P &&params, F &&func, Tasks &&... tasks)
| runs the given function asynchronously when the given predecessors finish
template<typename F, typename I> requires (!std::same_as<std::decay_t<I>, AsyncTask>)
tf::AsyncTask | silent_dependent_async (F &&func, I first, I last)
| runs the given function asynchronously when the given range of predecessors finish
template<TaskParamsConcept P, typename F, typename I> requires (!std::same_as<std::decay_t<I>, AsyncTask>)
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
void | corun ()
| corun all tasks spawned by this runtime with other workers
void | corun_all ()
| equivalent to tf::Runtime::corun - just an alias for legacy purpose
bool | is_cancelled ()
| queries if this runtime task has been cancelled

## Friends
class | Executor
class | FlowBuilder
class | PreemptionGuard
class | Algorithm

## Detailed Description

class to create a runtime task

A runtime object provides an interface for interacting with the scheduling system from within a task (i.e., the parent task of this runtime). It enables operations such as spawning asynchronous tasks, executing tasks cooperatively, and implementing recursive parallelism. The runtime guarantees an implicit join at the end of its scope, so all spawned tasks will finish before the parent runtime task continues to its successors.

tf::Executor executor(num_threads);
tf::Taskflow taskflow;
std::atomic<size_t> counter(0);
tf::Task A = taskflow.emplace([&](tf::Runtime& rt){
// spawn 1000 asynchronous tasks from this runtime task
for(size_t i=0; i<1000; i++) {
rt.silent_async([&](){ counter.fetch_add(1, std::memory_order_relaxed); });
}
// implicit synchronization at the end of the runtime scope
});
tf::Task B = taskflow.emplace([&](){
assert(counter.load(std::memory_order_relaxed) == 1000);
});
A.precede(B);
executor.run(taskflow).wait();
tf::Executor
class to create an executor
Definition executor.hpp:62
tf::FlowBuilder::emplace
Task emplace(C &&callable)
creates a static task
Definition flow_builder.hpp:1352
tf::Runtime
class to create a runtime task
Definition runtime.hpp:47
tf::Runtime::silent_async
void silent_async(F &&f)
runs the given function asynchronously without returning any future object
Definition runtime.hpp:671
tf::Runtime::executor
Executor & executor()
obtains the running executor
Definition runtime.hpp:621
tf::Task
class to create a task handle over a taskflow node
Definition task.hpp:263
tf::Task::precede
Task & precede(Ts &&... tasks)
adds precedence links from this to other tasks
Definition task.hpp:947
tf::Taskflow
class to create a taskflow object
Definition taskflow.hpp:64

Note
  To understand how Taskflow schedules a runtime task, please refer to Runtime Tasking.

## Member Function Documentation

## ◆ async() [1/2]

template<typename F>

auto tf::Runtime::async | ( | F && | f | ) |

runs the given callable asynchronously

Template Parameters

  F | callable type

Parameters

  f | callable object

This method creates an asynchronous task that executes the given function with the specified arguments. Unlike tf::Executor::async, the task created here is parented to the runtime object and is implicitly synchronized at the end of the runtime's scope. Applications may also call tf::Runtime::corun explicitly to wait for all asynchronous tasks spawned from the runtime to complete. For example:

std::atomic<int> counter(0);
taskflow.emplace([&](tf::Runtime& rt){
auto fu1 = rt.async([&](){ counter++; });
auto fu2 = rt.async([&](){ counter++; });
fu1.get();
fu2.get();
assert(counter == 2);
// spawn 100 asynchronous tasks from the worker of the runtime
for(int i=0; i<100; i++) {
rt.silent_async([&](){ counter++; });
}
// corun until the 100 asynchronous tasks have completed
rt.corun();
assert(counter == 102);
// do something else afterwards ...
});
tf::Runtime::async
auto async(F &&f)
runs the given callable asynchronously
Definition runtime.hpp:690
tf::Runtime::corun
void corun()
corun all tasks spawned by this runtime with other workers
Definition runtime.hpp:646

## ◆ async() [2/2]

template<typename P, typename F>

auto tf::Runtime::async | ( | P && | params,
|  | F && | f )

runs the given callable asynchronously

Template Parameters

  F | callable type
  P | task parameters type satisfying tf::TaskParamsConcept

Parameters

  params | task parameters
  f | callable

Similar to tf::Runtime::async, but takes a parameter of type tf::TaskParams to initialize the asynchronous task.

taskflow.emplace([&](tf::Runtime& rt){
auto future = rt.async("my task", [](){ return 10; });
assert(future.get() == 10);
});

## ◆ corun()

void tf::Runtime::corun | ( |  | ) |
| inline

corun all tasks spawned by this runtime with other workers

Coruns all tasks spawned by this runtime cooperatively with other workers in the same executor until all these tasks finish. Under cooperative execution, a worker is not preempted. Instead, it continues participating in the work-stealing loop, executing available tasks alongside other workers.

std::atomic<size_t> counter{0};
taskflow.emplace([&](tf::Runtime& rt){
// spawn 100 async tasks and wait
for(int i=0; i<100; i++) {
rt.silent_async([&](){ counter++; });
}
rt.corun();
assert(counter == 100);
// spawn another 100 async tasks and wait
for(int i=0; i<100; i++) {
rt.silent_async([&](){ counter++; });
}
rt.corun();
assert(counter == 200);
});

Only the parent worker of this runtime is allowed to call this corun.

## ◆ dependent_async() [1/4]

template<typename F, typename I>
requires (!std::same_as<std::decay_t<I>, AsyncTask>)

auto tf::Runtime::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.

taskflow.emplace([](tf::Runtime& rt){
std::array<tf::AsyncTask, 2> array {
rt.silent_dependent_async([](){ printf("A\n"); }),
rt.silent_dependent_async([](){ printf("B\n"); })
};
auto [C, fuC] = rt.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
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.run(taskflow).wait();
tf::Runtime::silent_dependent_async
tf::AsyncTask silent_dependent_async(F &&func, Tasks &&... tasks)
runs the given function asynchronously when the given predecessors finish
Definition runtime.hpp:710
tf::Runtime::dependent_async
auto dependent_async(F &&func, Tasks &&... tasks)
runs the given function asynchronously when the given predecessors finish
Definition runtime.hpp:754

## ◆ dependent_async() [2/4]

template<typename F, typename... Tasks>
requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)

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

runs the given function asynchronously when the given predecessors finish

Template Parameters

  F | callable type
  Tasks | tasks of type 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.

taskflow.emplace([](tf::Runtime& rt){
tf::AsyncTask A = rt.silent_dependent_async([](){ printf("A\n"); });
tf::AsyncTask B = rt.silent_dependent_async([](){ printf("B\n"); });
auto [C, fuC] = rt.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
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.run(taskflow).wait();
tf::AsyncTask
class to hold a dependent asynchronous task with shared ownership
Definition async_task.hpp:45

## ◆ dependent_async() [3/4]

template<TaskParamsConcept P, typename F, typename I>
requires (!std::same_as<std::decay_t<I>, AsyncTask>)

auto tf::Runtime::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::TaskParamsConcept
  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.

taskflow.emplace([](tf::Runtime& rt){
std::array<tf::AsyncTask, 2> array {
rt.silent_dependent_async("A", [](){ printf("A\n"); }),
rt.silent_dependent_async("B", [](){ printf("B\n"); })
};
auto [C, fuC] = rt.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
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.run(taskflow).wait();

## ◆ dependent_async() [4/4]

template<TaskParamsConcept P, typename F, typename... Tasks>
requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)

auto tf::Runtime::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::TaskParamsConcept
  F | callable type
  Tasks | tasks of type 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.

taskflow.emplace([](tf::Runtime& rt){
tf::AsyncTask A = rt.silent_dependent_async("A", [](){ printf("A\n"); });
tf::AsyncTask B = rt.silent_dependent_async("B", [](){ printf("B\n"); });
auto [C, fuC] = rt.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
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.run(taskflow).wait();

## ◆ executor()

Executor & tf::Runtime::executor | ( |  | ) |
| inline

obtains the running executor

The running executor of a runtime task is the executor that runs the parent taskflow of that runtime task.

tf::Executor executor;
tf::Taskflow taskflow;
taskflow.emplace([&](tf::Runtime& rt){
assert(&(rt.executor()) == &executor);
});
executor.run(taskflow).wait();
tf::Executor::run
tf::Future< void > run(Taskflow &taskflow)
runs a taskflow once

## ◆ schedule()

void tf::Runtime::schedule | ( | Task | task | ) |
| inline

schedules an active task immediately to the worker's queue

Parameters

  task | the given active task to schedule immediately

This member function immediately schedules an active task to the task queue of the associated worker in the runtime task. An active task is a task in a running taskflow. The task may or may not be running, and scheduling that task will immediately put the task into the task queue of the worker that is running the runtime task. Consider the following example:

tf::Task A, B, C, D;
std::tie(A, B, C, D) = taskflow.emplace(
[] () { return 0; },
[&C] (tf::Runtime& rt) { // C must be captured by reference
std::cout << "B\n";
rt.schedule(C);
},
[] () { std::cout << "C\n"; },
[] () { std::cout << "D\n"; }
);
A.precede(B, C, D);
executor.run(taskflow).wait();

The executor will first run the condition task `A` which returns `0` to inform the scheduler to go to the runtime task `B`. During the execution of `B`, it directly schedules task `C` without going through the normal taskflow graph scheduling process. At this moment, task `C` is active because its parent taskflow is running. When the taskflow finishes, we will see both `B` and `C` in the output.

Attention
  This method can only be called by the parent worker of this runtime, or the behavior is undefined. Furthermore, we currently do not support scheduling a runtime task.

## ◆ silent_async() [1/2]

template<typename F>

void tf::Runtime::silent_async | ( | F && | f | ) |

runs the given function asynchronously without returning any future object

Template Parameters

  F | callable type

Parameters

  f | callable

This function is more efficient than tf::Runtime::async and is recommended when the result of the asynchronous task does not need to be accessed via a std::future.

std::atomic<int> counter(0);
taskflow.emplace([&](tf::Runtime& rt){
for(int i=0; i<100; i++) {
rt.silent_async([&](){ counter++; });
}
rt.corun();
assert(counter == 100);
});

## ◆ silent_async() [2/2]

template<typename P, typename F>

void tf::Runtime::silent_async | ( | P && | params,
|  | F && | f )

runs the given function asynchronously without returning any future object

Template Parameters

  F | callable type

Parameters

  params | task parameters
  f | callable

Similar to tf::Runtime::silent_async, but takes a parameter of type tf::TaskParams to initialize the created asynchronous task.

taskflow.emplace([&](tf::Runtime& rt){
rt.silent_async("my task", [](){});
});

## ◆ silent_dependent_async() [1/4]

template<typename F, typename I>
requires (!std::same_as<std::decay_t<I>, AsyncTask>)

tf::AsyncTask tf::Runtime::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::Runtime::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`.

Taskflow.emplace([&](tf::Runtime& rt){
std::array<tf::AsyncTask, 2> array {
rt.silent_dependent_async([](){ printf("A\n"); }),
rt.silent_dependent_async([](){ printf("B\n"); })
};
rt.silent_dependent_async(
[](){ printf("C runs after A and B\n"); }, array.begin(), array.end()
);
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.wait_for_all();

## ◆ silent_dependent_async() [2/4]

template<typename F, typename... Tasks>
requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)

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

runs the given function asynchronously when the given predecessors finish

Template Parameters

  F | callable type
  Tasks | tasks of type 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::Runtime::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`.

taskflow.emplace([](tf::Runtime& rt){
tf::AsyncTask A = rt.silent_dependent_async([](){ printf("A\n"); });
tf::AsyncTask B = rt.silent_dependent_async([](){ printf("B\n"); });
rt.silent_dependent_async([](){ printf("C runs after A and B\n"); }, A, B);
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.wait_for_all();

## ◆ silent_dependent_async() [3/4]

template<TaskParamsConcept P, typename F, typename I>
requires (!std::same_as<std::decay_t<I>, AsyncTask>)

tf::AsyncTask tf::Runtime::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

  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::Runtime::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.

taskflow.emplace([](tf::Runtime& rt){
std::array<tf::AsyncTask, 2> array {
rt.silent_dependent_async("A", [](){ printf("A\n"); }),
rt.silent_dependent_async("B", [](){ printf("B\n"); })
};
rt.silent_dependent_async(
"C", [](){ printf("C runs after A and B\n"); }, array.begin(), array.end()
);
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.run(taskflow).wait();

## ◆ silent_dependent_async() [4/4]

template<TaskParamsConcept P, typename F, typename... Tasks>
requires (std::same_as<std::decay_t<Tasks>, AsyncTask> && ...)

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

runs the given function asynchronously when the given predecessors finish

Template Parameters

  F | callable type
  Tasks | tasks of type 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::Runtime::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.

taskflow.emplace([](tf::Runtime& rt){
tf::AsyncTask A = rt.silent_dependent_async("A", [](){ printf("A\n"); });
tf::AsyncTask B = rt.silent_dependent_async("B", [](){ printf("B\n"); });
rt.silent_dependent_async(
"C", [](){ printf("C runs after A and B\n"); }, A, B
);
}); // implicit synchronization of all tasks at the end of runtime's scope
executor.wait_for_all();

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

- taskflow/core/runtime.hpp