# tf::Runtime class
#include <taskflow/core/graph.hpp>

class to include a runtime object in a task

A runtime object allows users to interact with the scheduling runtime inside a task, such as scheduling an active task, spawning a subflow, and so on.

    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();

A runtime object is associated with the worker and the executor that runs the task.

## Derived classes

class Subflow
  class to construct a subflow graph from the execution of a dynamic task

## Constructors, destructors, conversion operators

~Runtime()
  destroys the runtime object

## Public functions

auto executor() -> Executor&
  obtains the running executor
auto 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) -> auto
  runs the given callable asynchronously
template<typename P, typename F>
auto async(P&& params, F&& f) -> auto
  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>
void silent_async_unchecked(F&& f)
  similar to tf::Runtime::silent_async but the caller must be the worker of the runtime
template<typename P, typename F>
void silent_async_unchecked(P&& params, F&& f)
  similar to tf::Runtime::silent_async but the caller must be the worker of the runtime
template<typename T>
void corun(T&& target)
  co-runs the given target and waits until it completes
template<typename P>
void corun_until(P&& predicate)
  keeps running the work-stealing loop until the predicate becomes true
void corun_all()
  corun all asynchronous tasks spawned by this runtime with other workers
template<typename... S, std::enable_if_t<all_same_v<Semaphore, std::decay_t<S>...>, void>* = nullptr>
void acquire(S && ... semaphores)
  acquires the given semaphores with a deadlock avoidance algorithm
template<typename I, std::enable_if_t<std::is_same_v<deref_t<I>, Semaphore>, void>* = nullptr>
void acquire(I first, I last)
  acquires the given range of semaphores with a deadlock avoidance algorithm
template<typename... S, std::enable_if_t<all_same_v<Semaphore, std::decay_t<S>...>, void>* = nullptr>
void release(S && ... semaphores)
  releases the given semaphores
template<typename I, std::enable_if_t<std::is_same_v<deref_t<I>, Semaphore>, void>* = nullptr>
void release(I first, I last)
  releases the given range of semaphores

## Function documentation

### tf::Runtime:: ~Runtime()

destroys the runtime object

Issues a tf::Runtime::corun_all to finish all spawned asynchronous tasks and then destroys the runtime object.

### Executor& tf::Runtime:: executor()

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();

### void tf::Runtime:: schedule( Task task)

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.

###
template<typename F>
auto tf::Runtime:: async(F&& f)

runs the given callable asynchronously

Template parameters
F | callable type
Parameters
f | callable object

The method creates an asynchronous task to launch the given function on the given arguments. The difference to tf::Executor::async is that the created asynchronous task pertains to the runtime object. Applications can explicitly issue tf::Runtime::corun_all to wait for all spawned asynchronous tasks to finish. 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.async([&](){ counter++; });
      }

      // wait for the 100 asynchronous tasks to finish
      rt.corun_all();
      assert(counter == 102);
    });

This method is thread-safe and can be called by multiple workers that hold the reference to the runtime. For example, the code below spawns 100 tasks from the worker of a runtime, and each of the 100 tasks spawns another task that will be run by another worker.

    std::atomic<int> counter(0);
    taskflow.emplace([&](tf::Runtime& rt){
      // worker of the runtime spawns 100 tasks each spawning another task
      // that will be run by another worker
      for(int i=0; i<100; i++) {
        rt.async([&](){
          counter++;
          rt.async([](){ counter++; });
        });
      }

      // wait for the 200 asynchronous tasks to finish
      rt.corun_all();
      assert(counter == 200);
    });

###
template<typename P, typename F>
auto tf::Runtime:: async(P&& params, F&& f)

runs the given callable asynchronously

Template parameters
P | task parameters type
F | callable type
Parameters
params | task parameters
f | callable

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

###
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 member function is more efficient than tf::Runtime::async and is encouraged to use when there is no data returned.

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

This member function is thread-safe.

###
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
P |
F | callable type
Parameters
params | task parameters
f | callable

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

###
template<typename F>
void tf::Runtime:: silent_async_unchecked(F&& f)

similar to tf::Runtime::silent_async but the caller must be the worker of the runtime

Template parameters
F | callable type
Parameters
f | callable

The method bypass the check of the caller worker from the executor and thus can only called by the worker of this runtime.

    taskflow.emplace([&](tf::Runtime& rt){
      // running by the worker of this runtime
      rt.silent_async_unchecked([](){});
      rt.corun_all();
    });

###
template<typename P, typename F>
void tf::Runtime:: silent_async_unchecked(P&& params, F&& f)

similar to tf::Runtime::silent_async but the caller must be the worker of the runtime

Template parameters
P | task parameters type
F | callable type
Parameters
params | task parameters
f | callable

The method bypass the check of the caller worker from the executor and thus can only called by the worker of this runtime.

    taskflow.emplace([&](tf::Runtime& rt){
      // running by the worker of this runtime
      rt.silent_async_unchecked("my task", [](){});
      rt.corun_all();
    });

###
template<typename T>
void tf::Runtime:: corun(T&& target)

co-runs the given target and waits until it completes

A target can be one of the following forms:

- a subflow task to spawn a subflow or
- a composable graph object with `tf::Graph& T::graph()` defined

    // co-run a subflow and wait until all tasks complete
    taskflow.emplace([](tf::Runtime& rt){
      rt.corun([](tf::Subflow& sf){
        tf::Task A = sf.emplace([](){});
        tf::Task B = sf.emplace([](){});
      });
    });

    // co-run a taskflow and wait until all tasks complete
    tf::Taskflow taskflow1, taskflow2;
    taskflow1.emplace([](){ std::cout << "running taskflow1\n"; });
    taskflow2.emplace([&](tf::Runtime& rt){
      std::cout << "running taskflow2\n";
      rt.corun(taskflow1);
    });
    executor.run(taskflow2).wait();

Although tf::Runtime::corun blocks until the operation completes, the caller thread (worker) is not blocked (e.g., sleeping or holding any lock). Instead, the caller thread joins the work-stealing loop of the executor and returns when all tasks in the target completes.

#### Attention

Only the worker of this tf::Runtime can issue corun.

###
template<typename P>
void tf::Runtime:: corun_until(P&& predicate)

keeps running the work-stealing loop until the predicate becomes 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.

#### Attention

Only the worker of this tf::Runtime can issue corun.

### void tf::Runtime:: corun_all()

corun all asynchronous tasks spawned by this runtime with other workers

Coruns all asynchronous tasks (tf::Runtime::async, tf::Runtime::silent_async) with other workers until all those asynchronous tasks finish.

    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_all();
      assert(counter == 100);

      // spawn another 100 async tasks and wait
      for(int i=0; i<100; i++) {
        rt.silent_async([&](){ counter++; });
      }
      rt.corun_all();
      assert(counter == 200);
    });

#### Attention

Only the worker of this tf::Runtime can issue tf::Runtime::corun_all.

###
template<typename... S, std::enable_if_t<all_same_v<Semaphore, std::decay_t<S>...>, void>* = nullptr>
void tf::Runtime:: acquire(S && ... semaphores)

acquires the given semaphores with a deadlock avoidance algorithm

Template parameters
S | semaphore type (tf::Semaphore)
Parameters
semaphores | semaphores

Coruns this worker until acquiring all the semaphores.

    tf::Semaphore semaphore(1);
    tf::Executor executor;

    // only one worker will enter the "critical_section" at any time
    for(size_t i=0; i<100; i++) {
      executor.async([&](tf::Runtime& rt){
        rt.acquire(semaphore);
        critical_section();
        rt.release(semaphore);
      });
    }

###
template<typename I, std::enable_if_t<std::is_same_v<deref_t<I>, Semaphore>, void>* = nullptr>
void tf::Runtime:: acquire(I first, I last)

acquires the given range of semaphores with a deadlock avoidance algorithm

Template parameters
I | iterator type
Parameters
first | iterator to the beginning (inclusive)
last | iterator to the end (exclusive)

Coruns this worker until acquiring all the semaphores.

    std::list<tf::Semaphore> semaphores;
    semaphores.emplace_back(1);
    semaphores.emplace_back(1);
    auto first = semaphores.begin();
    auto last  = semaphores.end();
    tf::Executor executor;

    // only one worker will enter the "critical_section" at any time
    for(size_t i=0; i<100; i++) {
      executor.async([&](tf::Runtime& rt){
        rt.acquire(first, last);
        critical_section();
        rt.release(first, last);
      });
    }

###
template<typename... S, std::enable_if_t<all_same_v<Semaphore, std::decay_t<S>...>, void>* = nullptr>
void tf::Runtime:: release(S && ... semaphores)

releases the given semaphores

Template parameters
S | semaphore type (tf::Semaphore)
Parameters
semaphores | semaphores

Releases the given semaphores.

    tf::Semaphore semaphore(1);
    tf::Executor executor;

    // only one worker will enter the "critical_section" at any time
    for(size_t i=0; i<100; i++) {
      executor.async([&](tf::Runtime& rt){
        rt.acquire(semaphore);
        critical_section();
        rt.release(semaphore);
      });
    }

###
template<typename I, std::enable_if_t<std::is_same_v<deref_t<I>, Semaphore>, void>* = nullptr>
void tf::Runtime:: release(I first, I last)

releases the given range of semaphores

Template parameters
I | iterator type
Parameters
first | iterator to the beginning (inclusive)
last | iterator to the end (exclusive)

Releases the given range of semaphores.

    std::list<tf::Semaphore> semaphores;
    semaphores.emplace_back(1);
    semaphores.emplace_back(1);
    auto first = semaphores.begin();
    auto last  = semaphores.end();
    tf::Executor executor;

    // only one worker will enter the "critical_section" at any time
    for(size_t i=0; i<100; i++) {
      executor.async([&](tf::Runtime& rt){
        rt.acquire(first, last);
        critical_section();
        rt.release(first, last);
      });
    }

Tab / T to search, Esc to close
…

Search for symbols, directories, files, pages or modules. You can omit any prefix from the symbol or file path; adding a `:` or `/` suffix lists all members of given symbol or directory.

Use ↓ / ↑ to navigate through the list, Enter to go. Tab autocompletes common prefix, you can copy a link to the result using ⌘ L while ⌘ M produces a Markdown link.

Sorry, nothing was found.