interface ov::threading::ITaskExecutor

Overview

Interface for Task Executor. OpenVINO uses ov::ITaskExecutor interface to run all asynchronous internal tasks. Different implementations of task executors can be used for different purposes: More…

#include <itask_executor.hpp>

template ITaskExecutor
{
    // methods

    virtual void run(Task task) = 0;
    virtual void run_and_wait(const std::vector<Task>& tasks);
};

// direct descendants

template ITaskExecutor;
template IStreamsExecutor;
class ImmediateExecutor;

Detailed Documentation

Interface for Task Executor. OpenVINO uses ov::ITaskExecutor interface to run all asynchronous internal tasks. Different implementations of task executors can be used for different purposes:

  • To improve cache locality of memory bound CPU tasks some executors can limit task’s affinity and maximum concurrency.

  • The executor with one worker thread can be used to serialize access to acceleration device.

  • Immediate task executor can be used to satisfy ov::ITaskExecutor interface restrictions but run tasks in current thread. Implementation should guaranty thread safety of all methods

Synchronization

It is ov::ITaskExecutor user responsibility to wait for task execution completion. The c++11 standard way to wait task completion is to use std::packaged_task or std::promise with std::future. Here is an example of how to use std::promise to wait task completion and process task’s exceptions:

// std::promise is move only object so to satisfy copy callable constraint we use std::shared_ptr
auto promise = std::make_shared<std::promise<void>>();
// When the promise is created we can get std::future to wait the result
auto future = promise->get_future();
// Rather simple task
InferenceEngine::Task task = [] {std::cout << "Some Output" << std::endl; };
// Create an executor
InferenceEngine::ITaskExecutor::Ptr taskExecutor = std::make_shared<InferenceEngine::CPUStreamsExecutor>();
if (taskExecutor == nullptr) {
    // ProcessError(e);
    return;
}
// We capture the task and the promise. When the task is executed in the task executor context
// we munually call std::promise::set_value() method
taskExecutor->run([task, promise] {
    std::exception_ptr currentException;
    try {
        task();
    } catch(...) {
        // If there is some exceptions store the pointer to current exception
        currentException = std::current_exception();
    }

    if (nullptr == currentException) {
        promise->set_value();  //  <-- If there is no problems just call std::promise::set_value()
    } else {
        promise->set_exception(currentException);    //  <-- If there is an exception forward it to std::future object
    }
});
// To wait the task completion we call std::future::wait method
future.wait();  //  The current thread will be blocked here and wait when std::promise::set_value()
                //  or std::promise::set_exception() method will be called.

// If the future store the exception it will be rethrown in std::future::get method
try {
    future.get();
} catch(std::exception& /\*e\*/) {
    // ProcessError(e);
}

Methods

virtual void run(Task task) = 0

Execute ov::Task inside task executor context.

Parameters:

task

A task to start

virtual void run_and_wait(const std::vector<Task>& tasks)

Execute all of the tasks and waits for its completion. Default run_and_wait() method implementation uses run() pure virtual method and higher level synchronization primitives from STL. The task is wrapped into std::packaged_task which returns std::future. std::packaged_task will call the task and signal to std::future that the task is finished or the exception is thrown from task Than std::future is used to wait for task execution completion and task exception extraction.

run_and_wait() does not copy or capture tasks!

Parameters:

tasks

A vector of tasks to execute