Continuation task without child

Continuation task without child

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[i]){
                  if(GT((*ppS)->Size,Epsilon*10)){
                       count++;
                       list.push_back(*new( c.allocate_child() ) tamanoArbolMinimo(&(c.sizes[i]),ppS));
                  }
                  else{
                       serList.push_back(*new serPPS(&(c.sizes[i]),ppS));
                  }
            }
            else c.sizes[i]=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.

 

publicaciones de 8 / 0 nuevos
Último envío
Para obtener más información sobre las optimizaciones del compilador, consulte el aviso sobre la optimización.
Best Reply

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.

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

"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.

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.

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.

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

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).

Deje un comentario

Por favor inicie sesión para agregar un comentario. ¿No es socio? Únase ya