tf::Runtime tf::Subflow taskflow/core/graph.hpp class friend class Executor Executor Executor class friend class FlowBuilder FlowBuilder FlowBuilder Executor & Executor& tf::Runtime::_executor _executor Worker & Worker& tf::Runtime::_worker _worker Node * Node* tf::Runtime::_parent _parent tf::Runtime::~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 & Executor & tf::Runtime::executor () 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::Executorexecutor; tf::Taskflowtaskflow; taskflow.emplace([&](tf::Runtime&rt){ assert(&(rt.executor())==&executor); }); executor.run(taskflow).wait(); Worker & Worker & tf::Runtime::worker () worker acquire a reference to the underlying worker void void tf::Runtime::schedule (Task task) schedule Task task schedules an active task immediately to the worker's queue 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::TaskA,B,C,D; std::tie(A,B,C,D)=taskflow.emplace( [](){return0;}, [&C](tf::Runtime&rt){//Cmustbecapturedbyreference 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. typename F auto auto tf::Runtime::async (F &&f) async F && f runs the given callable asynchronously F callable type 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){ autofu1=rt.async([&](){counter++;}); autofu2=rt.async([&](){counter++;}); fu1.get(); fu2.get(); assert(counter==2); //spawn100asynchronoustasksfromtheworkeroftheruntime for(inti=0;i<100;i++){ rt.async([&](){counter++;}); } //waitforthe100asynchronoustaskstofinish 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){ //workeroftheruntimespawns100taskseachspawninganothertask //thatwillberunbyanotherworker for(inti=0;i<100;i++){ rt.async([&](){ counter++; rt.async([](){counter++;}); }); } //waitforthe200asynchronoustaskstofinish rt.corun_all(); assert(counter==200); }); typename P typename F auto auto tf::Runtime::async (P &&params, F &&f) async P && params F && f runs the given callable asynchronously F callable type P task parameters type params task parameters f callable taskflow.emplace([&](tf::Runtime&rt){ autofuture=rt.async("mytask",[](){}); future.get(); }); typename F void void tf::Runtime::silent_async (F &&f) silent_async F && f runs the given function asynchronously without returning any future object F callable type 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(inti=0;i<100;i++){ rt.silent_async([&](){counter++;}); } rt.corun_all(); assert(counter==100); }); This member function is thread-safe. typename P typename F void void tf::Runtime::silent_async (P &&params, F &&f) silent_async P && params F && f runs the given function asynchronously without returning any future object F callable type params task parameters f callable taskflow.emplace([&](tf::Runtime&rt){ rt.silent_async("mytask",[](){}); rt.corun_all(); }); typename F void void tf::Runtime::silent_async_unchecked (F &&f) silent_async_unchecked F && f similar to tf::Runtime::silent_async but the caller must be the worker of the runtime F callable type 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){ //runningbytheworkerofthisruntime rt.silent_async_unchecked([](){}); rt.corun_all(); }); typename P typename F void void tf::Runtime::silent_async_unchecked (P &&params, F &&f) silent_async_unchecked P && params F && f similar to tf::Runtime::silent_async but the caller must be the worker of the runtime F callable type P task parameters type 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){ //runningbytheworkerofthisruntime rt.silent_async_unchecked("mytask",[](){}); rt.corun_all(); }); typename T void void tf::Runtime::corun (T &&target) 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-runasubflowandwaituntilalltaskscomplete taskflow.emplace([](tf::Runtime&rt){ rt.corun([](tf::Subflow&sf){ tf::TaskA=sf.emplace([](){}); tf::TaskB=sf.emplace([](){}); }); }); //co-runataskflowandwaituntilalltaskscomplete tf::Taskflowtaskflow1,taskflow2; taskflow1.emplace([](){std::cout<<"runningtaskflow1\n";}); taskflow2.emplace([&](tf::Runtime&rt){ std::cout<<"runningtaskflow2\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. Only the worker of this tf::Runtime can issue corun. typename P void void tf::Runtime::corun_until (P &&predicate) corun_until P && predicate keeps running the work-stealing loop until the predicate becomes true P predicate type 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. Only the worker of this tf::Runtime can issue corun. void void tf::Runtime::corun_all () 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){ //spawn100asynctasksandwait for(inti=0;i<100;i++){ rt.silent_async([&](){counter++;}); } rt.corun_all(); assert(counter==100); //spawnanother100asynctasksandwait for(inti=0;i<100;i++){ rt.silent_async([&](){counter++;}); } rt.corun_all(); assert(counter==200); }); Only the worker of this tf::Runtime can issue tf::Runtime::corun_all. typename... S S std::enable_if_t< all_same_v< Semaphore, std::decay_t< S >... >, void > * nullptr void void tf::Runtime::acquire (S &&... semaphores) acquire S &&... semaphores acquires the given semaphores with a deadlock avoidance algorithm S semaphore type (tf::Semaphore) semaphores semaphores Coruns this worker until acquiring all the semaphores. tf::Semaphoresemaphore(1); tf::Executorexecutor; //onlyoneworkerwillenterthe"critical_section"atanytime for(size_ti=0;i<100;i++){ executor.async([&](tf::Runtime&rt){ rt.acquire(semaphore); critical_section(); rt.release(semaphore); }); } typename I std::enable_if_t< std::is_same_v< deref_t< I >, Semaphore >, void > * nullptr void void tf::Runtime::acquire (I first, I last) acquire I first I last acquires the given range of semaphores with a deadlock avoidance algorithm I iterator type 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); autofirst=semaphores.begin(); autolast=semaphores.end(); tf::Executorexecutor; //onlyoneworkerwillenterthe"critical_section"atanytime for(size_ti=0;i<100;i++){ executor.async([&](tf::Runtime&rt){ rt.acquire(first,last); critical_section(); rt.release(first,last); }); } typename... S S std::enable_if_t< all_same_v< Semaphore, std::decay_t< S >... >, void > * nullptr void void tf::Runtime::release (S &&... semaphores) release S &&... semaphores releases the given semaphores S semaphore type (tf::Semaphore) semaphores semaphores Releases the given semaphores. tf::Semaphoresemaphore(1); tf::Executorexecutor; //onlyoneworkerwillenterthe"critical_section"atanytime for(size_ti=0;i<100;i++){ executor.async([&](tf::Runtime&rt){ rt.acquire(semaphore); critical_section(); rt.release(semaphore); }); } typename I std::enable_if_t< std::is_same_v< deref_t< I >, Semaphore >, void > * nullptr void void tf::Runtime::release (I first, I last) release I first I last releases the given range of semaphores I iterator type 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); autofirst=semaphores.begin(); autolast=semaphores.end(); tf::Executorexecutor; //onlyoneworkerwillenterthe"critical_section"atanytime for(size_ti=0;i<100;i++){ executor.async([&](tf::Runtime&rt){ rt.acquire(first,last); critical_section(); rt.release(first,last); }); } tf::Runtime::Runtime (Executor &, Worker &, Node *) Runtime Executor & e Worker & w Node * p typename P typename F auto auto tf::Runtime::_async (Worker &w, P &&params, F &&f) _async Worker & w P && params F && f typename P typename F void void tf::Runtime::_silent_async (Worker &w, P &&params, F &&f) _silent_async Worker & w P && params F && f 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::TaskA,B,C,D; std::tie(A,B,C,D)=taskflow.emplace( [](){return0;}, [&C](tf::Runtime&rt){//Cmustbecapturedbyreference 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. tf::Runtime_async tf::Runtime_executor tf::Runtime_parent tf::Runtime_silent_async tf::Runtime_worker tf::Runtimeacquire tf::Runtimeacquire tf::Runtimeasync tf::Runtimeasync tf::Runtimecorun tf::Runtimecorun_all tf::Runtimecorun_until tf::RuntimeExecutor tf::Runtimeexecutor tf::RuntimeFlowBuilder tf::Runtimerelease tf::Runtimerelease tf::RuntimeRuntime tf::Runtimeschedule tf::Runtimesilent_async tf::Runtimesilent_async tf::Runtimesilent_async_unchecked tf::Runtimesilent_async_unchecked tf::Runtimeworker tf::Runtime~Runtime