Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Employee
22 Views

What is wrong with this task usage?

I have one example: in my caller code, I would like to run a section of code in parallel, so I created a two task which then call a one task twice. Code looks like this:

class TwoTask: public tbb::task {
class TOneTask: public tbb::task {
public:
tbb::task* execute() {
// my implementation
}
OneTask( ...)
};
public:
tbb::task* execute() {
OneTask& a = *new( tbb::task::allocate_child() ) OneTask();
OneTask& b = *new( tbb::task::allocate_child() ) OneTask();
set_ref_count(2);
spawn(a);
return &b;
}
TwoTask () {}
};


// in my caller code
TwoTask &cefft = *new( tbb::task::allocate_root() )TwoTask (,,,);
tbb::task::spawn_root_and_wait(cefft);
cefft.destroy(cefft);


The problem is I always get segment violation when I run with 2 threads. But if I remove the spawn code, replace just the execute() function, so the code run in serial mode, everything is fine. There is no overlap memory write between two onetasks.

What is wrong with this code? Please help.
Thanks.
0 Kudos
4 Replies
Highlighted
Black Belt
22 Views

How about:
[cpp]tbb::task* execute() {
set_ref_count(2/*spawn*/+1/*wait_for_all*/);
OneTask& a = *new( tbb::task::allocate_child() ) OneTask();
OneTask& b = *new( tbb::task::allocate_child() ) OneTask();
spawn(a);
spawn_and_wait_for_all(b);
return NULL;
}
[/cpp]
I don't see how destroy() would not cause a problem in the caller code, though, because spawn_root_and_wait() should have destroyed its argument.
0 Kudos
Highlighted
Employee
22 Views

Quoting - Raf Schietekat
How about:
[cpp]tbb::task* execute() {
set_ref_count(2/*spawn*/+1/*wait_for_all*/);
OneTask& a = *new( tbb::task::allocate_child() ) OneTask();
OneTask& b = *new( tbb::task::allocate_child() ) OneTask();
spawn(a);
spawn_and_wait_for_all(b);
return NULL;
}
[/cpp]
I don't see how destroy() would not cause a problem in the caller code, though, because spawn_root_and_wait() should have destroyed its argument.

Sorry, I am not sure I get it. Do you mean this is the execute for TwoTask? How about caller code? I should not spawn in the caller code?

Are you saying the "destroy" is not necessary?
0 Kudos
Black Belt
22 Views

Yes, this should be TwoTask::execute(), and in the caller code I would not call destroy() (does it really cause no problems?), but the rest remains the same.

(Added) Have a look at "Simple Example: Fibonacci Numbers" in the Tutorial.
0 Kudos
Highlighted
22 Views

Raf is right. A TBB task is by default destroyed after execution, unless the programmer explicitly recycled it. So the original code has two problems: 1) child tasks decrement reference count of a destroyed parent, and 2) explicit call to destroy() at the end is foralready destroyed task. The second one is obvious to fix. The first one should be fixed either with explicit wait for children (i.e. spawn_and_wait_for_all as Raf suggested; note that the reference counter should be 3 in this case) or with recycling the TwoTask as continuation (note that then it will run for a second time after children completion, and take care that this second invocation is a no-op).

In just-released TBB 2.2, more user-friendly ways to run code sections in parallel are available, namely tbb::parallel_invoke function and tbb::task_group class.
0 Kudos