> We're having a deadlocking problem and I wonder if our usage of wait_for_all() and spawn() is causing the problem.
Yes it is. You should first spawn child tasks, and only then wait for their completion; and you do it in the wrong order.
Also I would recommend to allocate a special task for waiting; I have never seen (and never tried :)) using task::self() the way you do, and off the top of my head I can't say whether it will work as expected. Anyway, a recommended pattern for that is the following:
tbb::empty_task * t = new (tbb::task::allocate_root()) tbb::empty_task;
// allocate and spawn a child of t
tbb::empty_task * c = new (t.allocate_child()) tbb::empty_task;
// do something else
And with TBB 2.2, we recommend even a simpler pattern to be used in most cases:
tg.run( /*specify a function object here*/ );
// do something else
"Right, but I just used the same code as the author of the topic :)"
I don't want to study the code further until the author has revised it in the light of what you found, because I don't like the possible double-checked locking (which has been shown not to be reliable unless, e.g., the variable is atomic) and the number of explicit release() calls (code seems so much more accessible and maintainable with the use of locked block scopes instead), and the weird initialized_/initializing_ pair of variables, but I did think I recognised that the intent was to delay spawning as a task-oriented equivalent of waiting. My suggestion was to destroy() instead of spawn() (cheaper and with immediate effect), but of course even if the built-in deadlock is corrected there is still be an issue of required concurrency where TBB is all about optional parallelism instead, so the empty_task should probably be destroy()'ed from a user thread rather than another task.
(Added) Sounds a bit stern, but I only meant that it doesn't seem very useful to guess at this point.
So, the general question is this:
With tbb, what's the best way to have task A wait for a resource that's in use by task B, and then be woken up when task B is finished? Similar to a mutex but task-based so task A's thread can go do something else...