I'm working on a message reading and handling application trying to solve threading by TBB only.
Imagine a root task being started in a main loop like this:
usleep(100 * 1000);
#endifPacketReadingTask& task = *new(tbb::task::allocate_root()) PacketReadingTask();tbb::task::spawn_root_and_wait(task);
The root task will read the next available UDP packet from the receiving buffer, create a
continuation task and allocate a child task for the handling of the message. If a message
was handled, the task will also recycle itself as a child of the continuation task.
This means that as long as there are packets, it will continue recursively creating tasks
All that works pretty well, but here is the problem:
If there is a particular packet handling taking very long (let's say 2 seconds) and there
are no more packets available at a certain moment after that, it won't be able to
check for new packets until all children of the root task are finished.
I was wondering if there is a way to spawn root tasks and continue with the code
directly after that?
Or maybe I have to think in a different way to solve my problem?
Any tip would be appreciated.
Thanks in advance,
You might try the following:
- before your while(1) loop, allocate a tbb::empty_task object as a root task. It will not be actually spawned, but used as a parent for all packet processing tasks. Set the reference count of this task (let's call it parental_task) to 1, to prevent its execution.
- whenever you create a task that should process a packet, use allocate_additional_child_of(parental_task). Thus you do not introduce dependencies between your PacketReadingTask and packet processing child tasks. Note that allocate_child() can not be used when different threads allocate and execute childs of the same parent, like in this situation.
- instead of recycling PacketReadingTask as a child, use recycle_to_reexecute.
- you may want to return pointer to one of the child tasks (or a single child) as the result of PacketReadingTask::execute(), instead of spawning this task. The technique is called "scheduler bypassing". The returned child task will be executed right after, while the recycled PacketReadingTask can be stolen by another thread.
- in PacketReadingTask, if it does not find anything in the buffer, return NULL without recycling. Thus you will go to the while(1) loop and wait (sleep) there for some time.
- After your while(1) loop finishes, call parental_task.wait_for_all() to ensure that all its children are executed; then call destroy_task for it.
I must say that what I described to you above is quite similar to the internals of TBB's parallel_while and pipeline algorithms. So, you might want to consider these for your application. Though I am not sure if the need to wait on new data in the receiving buffer will fit well into these algorithms.
This seems to work, although in my tests it seems to be rather inefficient compared to just having 2 threads, each one reading and handling packets in parallel.
But it's good to know that things like that are possible with TBB. Thanks again, it's appreciated.