Taskflow allows tasks to interact with the scheduling runtime by taking a runtime object as an argument. This feature is particularly useful for implementing recursive parallel algorithms that create tasks dynamically.
A runtime task is a special type of task that interacts directly with the Taskflow scheduling runtime through a tf::Runtime object. By taking a tf::Runtime& parameter, a runtime task can dynamically create and manage other tasks during its execution. This capability enables flexible and expressive patterns such as recursive parallelism, cooperative task execution, and dynamic task spawning based on runtime conditions. For example, the code below creates a runtime task and acquires its running executor via tf::Runtime::executor():
Similar to tf::Executor, tf::Runtime allows you to create asynchronous tasks using tf::Runtime::async or tf::Runtime::silent_async. Asynchronous tasks spawned from a runtime task are logically parented to that runtime and are implicitly synchronized at the end of the runtime's scope. For example, the code below creates a runtime task A that spawns 1,000 asynchronous tasks during its execution. These 1,000 asynchronous tasks will be implicitly joined before task A completes, ensuring that all of them finish before the runtime proceeds to task B.
In addition to tf::Runtime::async and tf::Runtime::silent_async, you can create a dynamic task graph from a tf::Runtime using tf::Runtime::dependent_async and tf::Runtime::silent_dependent_async. For example, the code below creates a linear chain of dependent-async tasks from the execution context of a runtime:
The tf::Runtime::corun method enables cooperative execution within a runtime task. It explicitly waits for all tasks spawned through the same tf::Runtime object to complete before continuing execution. This is particularly useful when a runtime task launches multiple subtasks that must finish before the next phase of computation. Similar to tf::Executor::corun, the calling worker does not block; instead, it continues to execute available tasks from the executor, cooperating with other workers through the work-stealing loop.
In this example, all 100 asynchronous tasks spawned by the runtime are guaranteed to complete before the call to corun returns. This cooperative execution model is especially useful for expressing multi-phase or hierarchical parallel algorithms that require intermediate synchronization within a single runtime task.
Each asynchronous task can itself be a runtime task, enabling recursive task creation and dynamic parallelism. This model is particularly powerful for implementing divide-and-conquer algorithms, such as parallel sort, graph traversal, and recursion. For instance, the example below demonstrates a parallel recursive implementation of Fibonacci numbers using recursive asynchronous tasking with tf::Runtime:
The figure below shows the execution diagram, where the task with suffix *_1 represents the left child spawned by its parent runtime.