Alex Alex - 2 months ago 14
C++ Question

TBB task blocked when not recursing any further

struct root: public task
{
root(){}
tbb::task* execute(){
tbb::task_list l;
tbb::task& c = *new(allocate_continuation()) tbb::empty_task;
int count=0;
// do what this task should do
while(condition){
l.push_back(*new(c.allocate_child())root());
++count;
}
if(count){
c.set_ref_count(count);
c.spawn(l);
}
return NULL;
}
};
main(){
tbb::task_scheduler_init s();
root& r = *new(tbb::task::allocate_root())root();
tbb::task::spawn_root_and_wait(r);
return 0;
}


Hi, I've define a TBB task "root" that does some work and then recurses further if the
condition
in
while
loop is satisfied. If
count==0
after the
while
loop which means no further recursion is needed, then I won't generate more tasks.

Because what a
root
task does needs no continuation update, so I use
tbb::empty_task
to be the continuation task.

The problem is the execution is blocked by doing so. The program can not return normally. For now, my solution is:

if(!count){
l.push_back(*new(c.allocate_child())tbb::empty_task);
c.spawn(l);
c.set_ref_count(1);
}


However, I presume the price is the performance. I desire to know why the original code doesn't work. It worked in another circumstance where the continuation task is not an empty task. I don't quite get the logic in there. Thank you for any comments.

Answer

One problem is that when count==0, the task c is lost since it has no children and it is not spawned. The right way to deallocate a task is to call tbb::task::destroy. But since you allocated it as a continuation, the responsibility to signal back to the spawn_root_and_wait that work is done is transferred to the task c. And since the task is lost, the signal is lost too which leads to the deadlock you observe. To do the signalling efficiently for the count==0 case, just return the pointer to this c task as a result of root::execute() which is similar to doing the spawn(c) but avoids additional overheads for the synchronization.

Alternative is to do c.parent()->decrement_ref_count() and call to destroy(c) manually.

Comments