We have a couple of applications which all share the same basic task model, and we are struggling to get TBB to behave as desired. In this model we want to dispatch tasks on an on-going basis, potentially “forever” (i.e. until the machine or software fails) but with the capability to reach the end. In other words we have a stream of tasks, and they are all fire and forget... except for the need to wait for them all to finish at the end. Sometimes tasks will want to spawn more tasks as part of the same stream. We have tried many things using TBB, but haven’t found an approach that is simple and natural.
The ideal model would be to create a task_group and call its run method for each task. When we reach the end the main thread (which is handling I/O & event loop) it simply calls wait() on the group. All the tasks finish and the thread can terminate the application. This approach works... for a while. What appears to be happening, however, is that tasks finish running but are never deallocated and removed from the task_group. Eventually memory problems result.
Our current solution is a hack — we create a circular buffer of task_groups and a thread that waits for the oldest, destroys it, replaces it, and then advances an index that task spawners are using to choose the task_group to spawn into. This is a pretty horrid and fragile solution, and it would preferable to just have one task_group that we spawn into and which automatically deallocates tasks which complete.
Have we simply missed a feature in TBB? Is there a simple and elegant way to accomplish this? It seems very surprising that this isn’t more natural in TBB because this is the kind of basic task model I have always used in all my applications — far more prevalent than parallel for loops, and all the other fancy features TBB supports!