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;
}
condition
while
count==0
while
root
tbb::empty_task
if(!count){
l.push_back(*new(c.allocate_child())tbb::empty_task);
c.spawn(l);
c.set_ref_count(1);
}
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.