Cancellation Without An Exception#
To cancel an algorithm but not throw an exception, use the expression current_context()->cancel_group_execution().
The part current_context() references the task_group_context* of the currently executing task if any on the current thread.
Calling cancel_group_execution() cancels all tasks in its task_group_context, which is explained in more detail in Cancellation and Nested Parallelism.
The method returns true if it actually causes cancellation, false if the task_group_context was already cancelled.
The example below shows how to use current_context()->cancel_group_execution().
#include "oneapi/tbb.h"
#include <vector>
#include <iostream>
using namespace oneapi::tbb;
using namespace std;
vector<int> Data;
struct Update {
    void operator()( const blocked_range<int>& r ) const {
        for( int i=r.begin(); i!=r.end(); ++i )
            if( i<Data.size() ) {
                ++Data[i];
            } else {
                // Cancel related tasks.
                if( current_context()->cancel_group_execution() )
                    cout << "Index " << i << " caused cancellation\n";
                return;
            }
    }
};
int main() {
    Data.resize(1000);
    parallel_for( blocked_range<int>(0, 2000), Update());
    return 0;
}
task_group Cancellation Example#
The interface to a task_group provides shortcuts to access its asoociated task_group_context.
The function oneapi::tbb::task_group::cancel() cancels the task_group_context associated
with the task_group instance. And the free function
oneapi::tbb::is_current_task_group_canceling() returns true if the innermost task_group
executing on the calling thread is cancelling its tasks.
Here is an example of how to use task cancellation with oneapi::tbb::task_group. This code
uses struct TreeNode and the function sequential_tree_search that are described in
Creating Tasks with parallel_invoke.
The function parallel_tree_search_cancellable_impl cancels the task_group when a result
is found.
void parallel_tree_search_cancellable_impl(tbb::task_group& tg, TreeNode* node, int target, 
                                           std::atomic<TreeNode*>& result, 
                                           size_t depth_threshold = initial_depth_threshold) {
    // tbb::is_current_task_group_canceling() checks asoociated task_group_context
    if (node && !tbb::is_current_task_group_canceling()) {
        if (node->value == target) {
            result.store(node); // overwrite is ok since any result is valid
            // cancel the task_group_context associated with task_group
            tg.cancel(); // multiple cancellations are ok due to single wait
        } else if (depth_threshold == 0) {
            sequential_tree_search(node, target, result);
            if (result.load() != nullptr) {
                // cancel the task_group_context associated with task_group
                tg.cancel(); // multiple cancellations are ok due to single wait
            }
        } else {
            // Run on left and right subtrees in parallel
            tg.run([node, target, &result, &tg, depth_threshold] {
                parallel_tree_search_cancellable_impl(tg, node->left, target, result,
                                                      depth_threshold - 1);
            });
            tg.run([node, target, &result, &tg, depth_threshold] {
                parallel_tree_search_cancellable_impl(tg, node->right, target, result,
                                                      depth_threshold - 1);
            });
        }
    }
}
TreeNode* parallel_tree_search_cancellable(TreeNode* root, int target) {
    if (!root) return nullptr;
    
    std::atomic<TreeNode*> result{nullptr};
    tbb::task_group tg;
    
    parallel_tree_search_cancellable_impl(tg, root, target, result);
    
    tg.wait();
    return result.load();
}
The call to tg.cancel() cancels the tasks in the task_group that have been submitted but
have not started to execute. The task scheduler will not execute these tasks. Those tasks that
have already started will not be interrupted but they can query the cancellation status of the
task_group by calling oneapi::tbb::is_current_task_group_canceling() and exit early if
they detect cancellation.
