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

Can I separate task::spawn_root_and_wait() into 2 calls?

mwhouser
Beginner
615 Views

I want to separate spawn_root_and_wait() into 2 calls: spawn and wait. I need to do some processing in the main thread while my tasks are working.

How can I do this?

...Matt

0 Kudos
7 Replies
Dmitry_Vyukov
Valued Contributor I
615 Views
Quoting - mwhouser

I want to separate spawn_root_and_wait() into 2 calls: spawn and wait. I need to do some processing in the main thread while my tasks are working.

How can I do this?

...Matt

Create 2 root tasks: first is your current root task, and second is your processing that you need to do concurrently with tasks. Spawn both tasks simultaneously - there is spawn_root_and_wait() overload that accepts a list of tasks.

0 Kudos
mwhouser
Beginner
615 Views
Quoting - Dmitriy V'jukov

Create 2 root tasks: first is your current root task, and second is your processing that you need to do concurrently with tasks. Spawn both tasks simultaneously - there is spawn_root_and_wait() overload that accepts a list of tasks.

The work that I need to do must be done on the UI thread, so I cannot spawn off a second thread to do this work (I tried this solution already).

Analternative is to create custom messages to be posted from worker threads. However, that would require a significant amount of additional work. If I can separate the spawn and wait calls, then all I need is 10 lines of code to marshall info between the worker threads and the UI thread: much simpler solution.

For now, I'm creating a worker thread (non-tbb) that spawns the real worker tasks. While I wait for the non-tbb thread to complete, I do the work I need on the UI thread. Not the most elegant, but it works. Having separate spawn and wait calls (like we do when we're already inside a task) would simplify things greatly.

...Matt

0 Kudos
ARCH_R_Intel
Employee
615 Views
You can get the effect of a non-blocking spawn_root call by providing a dummy root task. The dummy root does not actually run. It merely provides a hook to spawn tasks and hang the wait on. Indeed, the need for the "hook" is why there is no separate spawn_root() call.

An example of the trick is shown in my blog on implementing "task_group" in TBB ( http://software.intel.com/en-us/blogs/2008/07/02/implementing-task_group-interface-in-tbb/). The TBB implementation shown there lets you spawn subsidiary tasks while letting the main thread continue until it needs to wait on the other tasks.

Below is a complete example that uses the dummy root trick. The wait is done by the destructor. I wrote the example before I realized that task_group would solve the problem. Note that if there is only a single thread available (i.e. master and no workers) then the off to side work will not be done until the destructor is invoked. If you need to fire up multiple tasks off to the side, consider using the "task_group" code instead.
- Arch

#include "tbb/task_scheduler_init.h"
#include "tbb/task.h"
#include

class OtherThing: public tbb::task {
/*override*/ task* execute() {
for( volatile int i=0; i<50000000; ++i )
if( i%10000000==0 )
printf("Doing thing off to siden");
return NULL;
}
};

class DoTaskOffToSide {
tbb::empty_task* root;
public:
DoTaskOffToSide() {
root = new( tbb::task::allocate_root() ) tbb::empty_task;
// 2 = 1 for child and 1 for wait
root->set_ref_count(2);
tbb::task& child = *new( root->allocate_child() ) OtherThing;
root->spawn(child);
}
~DoTaskOffToSide() {
root->wait_for_all();
root->destroy(*root);
}
};

int main() {
tbb::task_scheduler_init init;
{
DoTaskOffToSide s;
for( volatile int i=0; i<50000000; ++i )
if( i%10000000==0 )
printf("Doing main thing on original threadn");
}
printf("Donen");
}

0 Kudos
mwhouser
Beginner
615 Views
This does the trick. However, it's not working how I hoped it would work. My work on the main thread updates aprogress bar. So this thread blocks waiting for a signal from the worker threads. When it receives the signal, it updates the progress bar.
The problem is that if there is only 1 thread setup with the scheduler, then my UI thread is never signalled because the workers never work. You mentioned that.
Currently, I amback to using my non-tbb thread to wait for the tbb threads. However, I am noticing that sometimes those threads never run (again when only 1 task is running) because I guess the task scheduler has determined that there's too much going on in the main thread, then the main thread blocks and the worker threads never work. So I end up in deadlock (sometimes).
How does one update a progress bar reliably? I cannot use SendMessage from a worker thread... it always blocks and never returns. PostMessage doesn't block, but my UI never updates because my main thread isn't pumping messages, so the messages are not handled until the main thread completes the work and returns to it's message pump.
And the task scheduler sometimes thinks that my main thread is occupying too much CPU for it to run any of the worker tasks.
Is there a solution?
...Matt

0 Kudos
Dmitry_Vyukov
Valued Contributor I
615 Views
Quoting - mwhouser
This does the trick. However, it's not working how I hoped it would work. My work on the main thread updates aprogress bar. So this thread blocks waiting for a signal from the worker threads. When it receives the signal, it updates the progress bar.
The problem is that if there is only 1 thread setup with the scheduler, then my UI thread is never signalled because the workers never work. You mentioned that.

In '1 thread' setup all the work must be done in the thread that execute spawn_root_and_wait(). There must be NO DIFFERENCE for you, and there must be NO DEADLOCKS if there is no worker-threads (if you are doing all correctly). I can suggest that you describe you algorithm and setup in more detail - infrequent deadlocks suggest that you have some logic error in your program.

0 Kudos
Dmitry_Vyukov
Valued Contributor I
615 Views
Quoting - mwhouser
Currently, I amback to using my non-tbb thread to wait for the tbb threads. However, I am noticing that sometimes those threads never run (again when only 1 task is running) because I guess the task scheduler has determined that there's too much going on in the main thread, then the main thread blocks and the worker threads never work. So I end up in deadlock (sometimes).

Occasional deadlocks CANNOT be induced by TBB scheduler (if there is no serious errors), only by your program logic.

0 Kudos
Dmitry_Vyukov
Valued Contributor I
615 Views
Quoting - mwhouser
How does one update a progress bar reliably? I cannot use SendMessage from a worker thread... it always blocks and never returns. PostMessage doesn't block, but my UI never updates because my main thread isn't pumping messages, so the messages are not handled until the main thread completes the work and returns to it's message pump.

If you are using separate non-tbb thread to wait for tbb, then why your main thread is not processing messages? It must spawn separate thread and return to message processing, no? And PostMessage() must do the work.

0 Kudos
Reply