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

using std:shared_ptr with tbb:task

Vitaliy_K_1
Beginner
487 Views

Hello,

what is the right way to use std::shared_ptr with tbb::task? Is it possible or does it make sence? I need to access tbb::task through smart pointer, because the project I'm working on utilizes smart pointers and I don't want to rewrite everything. Here is the modified Fibonacci-example. I get "terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  std::bad_weak_ptr" during the execution.

I'm grateful for every suggestion.

#include "tbb/tbb.h"
#include <iostream>
#include <memory>

using namespace tbb;


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

class FibTask: public task, public std::enable_shared_from_this<FibTask> {
public:
    const long n;
    long* const sum;
    FibTask(long n_, long* sum_) :
            n(n_), sum(sum_) {
    }
    task* execute() {
        if (n < 10) {
            *sum = SerialFib(n);
        } else {
            long x, y;
          
            FibTask* a1 = new (allocate_child()) FibTask(n - 1, &x);
            FibTask* b1 = new (allocate_child()) FibTask(n - 2, &y);


            std::shared_ptr<FibTask> a = a1->shared_from_this();
            std::shared_ptr<FibTask> b = b1->shared_from_this();

            set_ref_count(3);

            FibTask& aa = *a.get();
            FibTask& bb = *b.get();
            spawn(bb);
            spawn_and_wait_for_all(aa);
            *sum = x + y;
        }
        return NULL;
    }
};

long ParallelFib(long n) {
    long sum;
  
    FibTask* a1 = new(task::allocate_root()) FibTask(n, &sum);
    std::shared_ptr<FibTask> aa = a1->shared_from_this();
    FibTask& a = *aa.get();


    task::spawn_root_and_wait(a);


 
    return sum;
}

int main()
{
    std::cout << ParallelFib(20);
}

0 Kudos
1 Reply
RafSchietekat
Valued Contributor III
487 Views
Does that even work without using TBB, invoking shared_from_this() without a previously existing shared_pointer to *this ("There shall be at least one shared_ptr instance p that owns &t.")? I have a hunch that the exception may already be thrown at the time of assignment to a, but I haven't explored this further, so don't quote me on that (I'd be mildly interested to know if you or somebody else investigates that further). Anyway, a spawned task will ultimately be deleted, with only a reference count in the parent keeping track of its death, and this would happen out of sight from any shared_ptr pretending ownership. You could probably still use shared pointers if you want to, but then you would also have to adapt the task to be recycled, and it seems somewhat pointless without any other reason to recycle. Sharing also sounds nice in social settings and in abstract theory, but it is anathema to scalability on real hardware with coherent-cache overheads, which is another reason why it was not and should not be introduced here, in a toolkit that emphasises performance and scalability over other niceties like fairness. My advice is to accept the situation and adapt your code.
0 Kudos
Reply