Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.

Assertion hit when handling exceptions in Task Scheduler

Steve_E_
Beginner
507 Views

I have an application where I want to use the TBB Task Scheduler. I
need to be able to handle exceptions in the following way:

a).  Exceptions that occur within the tasks need to propagate through and
cancel the tasks within the algorithm.
b).  Exceptions need to be able to be caught at the top most invocation
of the Task Scheduler for the set of tasks (i.e. at the
site of tbb::task::spawn_root_and_wait method call).

In order to factor out my application code, I modified the Fibonacci
example found both in the book "Intel Threading Build Blocks" and the
Intel TBB Developer Guide.

Here is the code:

////////////Start of Code
// Parallel example of Fibonacci Number Calculation
// Used to understand exception handling with
// the TBB custom scheduler.

#include "tbb/tbb.h"

#include <system_error>
#include <exception>
#include <iostream>

long SerialFib (long n)
{
    // Simulated exception  - we throw a "Resource temporarily unavailable"
    // After some parallel processing is occurring.
    if (n == 14)
        throw std::system_error (EAGAIN, std::system_category());

    if (n < 2)
        return n;
    else
        return SerialFib(n - 1) + SerialFib (n - 2);
}

const int cutoff = 16;

class FibTask: public tbb::task
{
public:
    const long d_n;
    long * const d_sum;
    FibTask(long n, long * sum): d_n(n), d_sum(sum){}
    virtual ~FibTask(){}

    tbb::task * execute()
    {
        if (d_n < cutoff)
        {
            std::cout << "SERIAL..." << std::endl;
            *d_sum = SerialFib(d_n);
        }
        else
        {
            std::cout << "PARALLEL..." << std::endl;
            long x, y;
            FibTask & a = *new(allocate_child()) FibTask(d_n - 1, &x);
            FibTask & b = *new(allocate_child()) FibTask(d_n - 2, &y);
            set_ref_count(3);
            spawn(b);
            spawn_and_wait_for_all(a);
            *d_sum = x+y;
        }
        return nullptr;
    }
};

long ParallelFib(long n)
{
    long sum;

    try
    {
        FibTask & a = * new(tbb::task::allocate_root()) FibTask(n, &sum);
        tbb::task::spawn_root_and_wait(a);
        return sum;
    }
    catch(std::exception & e)
    {
        std::cout << "EXCEPTION PARALLEL CALL POINT: " << e.what() << std::endl;
        throw;
    }
    catch(...)
    {
        std::cout << "EXCEPTION: PARALLEL CALL POINT..." << std::endl;
        throw;
    }
}

const int from_range = 20;
const int to_range = 22;
main()
{
    std::cout << "In Main..." << std::endl;

    try
    {
        for(int x = from_range; x < to_range;++x)
        {
            std::cout << ParallelFib(x) << std::endl;
        }
    }
    catch(std::exception & e)
    {
        std::cout << "MAIN EXCEPTION: " << e.what() << std::endl;
    }
    catch(...)
    {
        std::cout << "MAIN EXCEPTION..." << std::endl;
    }
}
//////// End of Code

Here's the issue: in roughly 3 out of 5 runs of the program, the
following occurs:

Assertion !is_worker() || !CancellationInfoPresent(*my_dummy_task) failed on
line 663 of file ../../src/tbb/custom_scheduler.h
Detailed description: Worker's dummy task context modified


I have not be able to find any additional information on this assertion in
the TBB code.

My questions are:

1).  Is it possible to get the exception behavior I'm looking for from the
Task Scheduler?

2).  What is the cause of the assertion?

Thanks

0 Kudos
2 Replies
Alexei_K_Intel
Employee
507 Views

Hi Steve,

The exception handling is done automatically when parallel algorithms are used, e.g. tbb::parallel_for, tbb::task_group or tbb::flow::graph. If you want use tasks directly, you need to use tbb::task_group_context. In the example you just need to replace a line in ParallelFib:

FibTask & a = * new(tbb::task::allocate_root()) FibTask(n, &sum);

With 

tbb::task_group_context ctx;
FibTask & a = * new(tbb::task::allocate_root(ctx)) FibTask(n, &sum);

As for a reason why you observe the assert. Each task is bind to a context. The child tasks are usually bound to a context of the parent tasks. However, the root task does not have parent and bound to a default context that is used by master and all worker threads for some purpose. So when an exception raised, it is caught into the default context and all worker threads see the exception in default context; however, it is a logic error in TBB.

Regards, Alex

0 Kudos
Steve_E_
Beginner
507 Views

Thanks! That did it.

0 Kudos
Reply