Resumable tasks

Summary

Functions to suspend task execution at a specific point and signal to resume it later.

Syntax

tbb::task::suspend_point;
template < typename Func > void tbb::task::suspend( Func );
void tbb::task::resume( tbb::task::suspend_point );

Header

#define TBB_PREVIEW_RESUMABLE_TASKS 1
#include "tbb/task.h"

Note

The feature requires linkage with TBB preview binaries.

Description

I/O operations in their various forms may block TBB threads, decreasing overall CPU utilization. The tbb::task::suspend function called within a running task suspends execution of the task and switches the thread to participate in other TBB parallel work. This function accepts a user callable object with the current execution context tbb::task::suspend_point as an argument. This object must model the requirements from the following table:

Requirements for tbb::task::suspend functional object
Pseudo-signarute Semantics

Func::Func( const Func& )

Copy constructor.

void Func::operator()( tbb::task::suspend_point )

Body that accepts the current task execution point to resume later.

Pass the tbb::task::suspend_point context tag to the tbb::task::resume function to trigger a program execution at the suspended point. The tbb::task::resume function can be called at any point of an application, even on a separate thread. In this regard, this function acts as a signal for the TBB scheduler.

Note

Note, that there are no guarantees, that the same thread that called tbb::task::suspend will continue execution after the suspended point. However, these guarantees are provided for the outermost blocking TBB calls (such as tbb::parallel_for and tbb::flow::graph::wait_for_all) and tbb::task_arena::execute calls.

Example

TBB Flow Graph has a tbb::flow::async_node class to communicate with an external activity managed by the user or another runtime. This example shows, how to achieve the same purpose (as illustrated in tbb::flow::async_node example) but within a usual tbb::parallel_for region. Note, that a callback support or a dedicated thread is still required, but this approach is slightly simpler because explicit reserve/release_wait calls and flow graph instance are not needed.

#include <thread>
#include <iostream>

#define TBB_PREVIEW_RESUMABLE_TASKS 1
#include "tbb/task.h"
#include "tbb/parallel_for.h"
#include "tbb/concurrent_queue.h"

typedef int data_t;

class AsyncActivity {
    typedef std::pair<data_t*, tbb::task::suspend_point> work_t;

public:
    AsyncActivity() : m_asyncThread(&AsyncActivity::asyncLoop, this) {}

    ~AsyncActivity() {
        m_tagQueue.push({NULL, NULL});
        m_asyncThread.join();
    }

    void submit(data_t& data, tbb::task::suspend_point tag) {
        m_tagQueue.push({&data, tag});
    }

private:
    void asyncLoop() {
        work_t work;
        m_tagQueue.pop(work);
        while (work.second) {
            // Process an async work
            asyncWork(*work.first);
            // Signal to resume execution of the task referenced by the tbb::task::suspend_point
            tbb::task::resume(work.second);
            // Process a next item
            m_tagQueue.pop(work);
        }
    }

    void asyncWork(data_t& data) {
        data++;
    }

    // Work items storage
    tbb::concurrent_bounded_queue<work_t> m_tagQueue;
    // Dedicated thread to process async work
    std::thread m_asyncThread;
};

data_t preStep(int data) {
    std::cout << "Pre step: " << data << std::endl;
    return data_t(data);
}

void postStep(data_t& data) {
    std::cout << "Post step: " << data << std::endl;
}

int main() {
    // Dedicated user-managed activity that processes async requests
    AsyncActivity async_activity;

    // Parallel computation
    tbb::parallel_for(0, 10, [&async_activity](int i) {
        data_t data = preStep(i);
        // Suspend the current task execution and capture the context
        tbb::task::suspend([&async_activity, &data] (tbb::task::suspend_point tag) {
            async_activity.submit(data, tag); // could be OpenCL/IO/Database/Network etc.
        }); // execution will be resumed after this function
        postStep(data);
    });

    return 0;
}
For more complete information about compiler optimizations, see our Optimization Notice.