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

Continuation task without child

asenjo
Innovator
572 Views

I'll be grateful if someone can give me some advice with the following task::execute() code:

task* tamanoArbolMinimo::execute(){
        PPSIMPLEX * arraySmpx;
        PPSIMPLEX ppS;
        INT tamArraySmpx;

        tamArraySmpx=getArraySmpx(arraySmpx,ppSX);

        tamAMinContinuation& c= *new(allocate_continuation()) tamAMinContinuation (result,tamArraySmpx);

        int count=0;
        task_list list;
        std::vector<serPPS> serList;

        for(int i=0; i<tamArraySmpx; i++){
            if(ppS=arraySmpx){
                  if(GT((*ppS)->Size,Epsilon*10)){
                       count++;
                       list.push_back(*new( c.allocate_child() ) tamanoArbolMinimo(&(c.sizes),ppS));
                  }
                  else{
                       serList.push_back(*new serPPS(&(c.sizes),ppS));
                  }
            }
            else c.sizes=COPY;       
        }
        delete[] arraySmpx;

        secTamanoArbolMinimo *a;
        if(serList.size()){
                count++;
                a=new( c.allocate_child() ) secTamanoArbolMinimo(serList);
        } 

        c.set_ref_count(count);
        spawn(list);
        if(serList.size()) return a;
        else return NULL;
  }

Basically the task can generate more parallel tasks, and/or a sequential task (secTamanoArbolMinimo) or none of them. The two questions are:

  1. What if count ends up being 0 (the task_list list and serList are empty)?. Is it safe to spawn an empty task_list?. Or should I conditionally do the spawn?
  2. Is the continuation task, c, being executed as soon as c.ref_count==0 even if it never had children?

Sorry if this is already covered elsewhere, but I could'n found a related question on the Web. If you happen to know the appropriate link or you have any additional recommendation, please do not hesitate to share it with me. Thanks a lot in advance.

 

0 Kudos
1 Solution
Alexey-Kukanov
Employee
572 Views

On spawning a task list, the very first operation is to check if it is empty, in which case spawn() immediately returns. So it's safe.

A continuation task with no children will not be spawned or executed. Essentially, it will be lost. If you need this task to be executed in any case, best of all is to return it for immediate execution; otherwise, you better destroy it.

View solution in original post

0 Kudos
7 Replies
Alexey-Kukanov
Employee
573 Views

On spawning a task list, the very first operation is to check if it is empty, in which case spawn() immediately returns. So it's safe.

A continuation task with no children will not be spawned or executed. Essentially, it will be lost. If you need this task to be executed in any case, best of all is to return it for immediate execution; otherwise, you better destroy it.

0 Kudos
asenjo
Innovator
572 Views

Alexey, thank you for your super-prompt reply! So, if there is no child I will just return &c. Thanks!

0 Kudos
RafSchietekat
Valued Contributor III
572 Views

"A continuation task with no children will not be spawned or executed."

That's not yet documented, and, as shown by this forum thread, it's not obvious, also because it's probably more a design choice (deliberate or otherwise) to avoid implementation complications than technical necessity.

0 Kudos
jiri
New Contributor I
572 Views

I think that technically the fact that "A continuation task with no children will not be spawned or executed." is not really an undocumented feature. There is nothing special about a continuation task after it is created (creation is "special", since it moves the successor pointer from the original task to the new continuation). As a result, it is governed by the normal task rules and as with any task, there are several ways in which you can make it execute, having children that eventually bring the reference count to zero being one of them. If none of these methods is used, the task does not run and if you don't destroy it, it is lost.

0 Kudos
RafSchietekat
Valued Contributor III
572 Views

The fact that you may be able to come to that conclusion does not mean that a Reference Manual shouldn't mention it. The implementation makes certain assumptions to keep things simple, and these assumptions should be made explicit. Otherwise, who's to say with perfect certainty but without sufficient experience or having studied the source code that TBB wouldn't obviously have provided for this corner case?

I would also mention something else that might not be obvious to everybody: if the current execute() spawns all its child tasks, instead of bypassing the scheduler for one of them by returning it, any actions in the parent's execute() after having spawned all child tasks would not "happen before" the continuation's execute(), if it would be acceptable at all to have such partially concurrent execution between original parent and continuation. Did I overlook any advice related to that?

Some maintenance seems to be in order, anyhow. Here's something I found under "Task Scheduler": "To solve this problem, the scheduler constrains a blocked thread such that it never executes a task that is less deep than its deepest blocked task." Hey, how does that solve the problem of unconstrained stack growth? Shouldn't it only execute tasks that are deeper? But TBB does not consider depth anymore, so this is moot... and the documentation should instead describe the current implementation.

0 Kudos
Anton_M_Intel
Employee
572 Views

One additional note: this Stack Overflow question is pretty much the same (I guess it was asked by you or on the same kind of problem). I've recommended to return the continuation task as result of execute() method as well. But please note that if you want to destroy it as Alexey suggested, you have to decrement the parent's ref_count in order to avoid the deadlock

0 Kudos
asenjo
Innovator
572 Views

Thank you Anton. Actually it wasn't me the one posting on Stack Overflow. I ended up returning the continuation task (just inserting this line: 

else if (!count) return &c;

between lines 36 and 37 in my code above).

0 Kudos
Reply