- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
We're having a deadlocking problem and I wonder if our usage of wait_for_all() and spawn() is causing the problem.
Basically, a task that is waiting for all its children never wakes up even though all the children (which are empty_tasks) have been spawned.
I believe that the spawned children are never executed and I'm wondering if the way we're spawning the children is the problem. If myObj is being initialized by task A, then a task (task B) that needs to wait for A creates a child, adds it to a list and calls wait_for_all(). Once A has finished, it loops over that list and spawns the children of B.
At first I figured there was a problem with another thread spawning B's children (since the docs say the owning thread must do the spawn), but neither mechanism of calling spawn() (see the code snippet below) triggered any warnings from the debug versions of the tbb libraries. Is it really valid to do the spawning as shown below?
I'm using tbb 2.2.13.
Thanks!
void myObj::init (myInfo & info)
{
if (initialized_) {
//
// fill info
//
return;
}
mutextype::scoped_lock lock (initMutex_);
if (initialized_) {
lock.release ();
//
// fill info
//
return;
}
if (initializing_) {
tbb::task& task = tbb::task::self ();
task.set_ref_count (2);
tbb::empty_task * c = new (task.allocate_child ()) tbb::empty_task;
waiters_.push_back (c); // waiters_ is a member of myObj
lock.release ();
task.wait_for_all ();
//
// fill info
//
return;
}
initializing_ = true;
lock.release ();
//
// perform long initialization
// and fill info
//
lock.acquire (initMutex_);
initialized_ = true;
initializing_ = false;
lock.release ();
for (wsize_type i = 0; i < waiters_.size (); ++i) {
tbb::task& c = *waiters_;
c.parent ()->spawn (c);
// tbb::task::self ().spawn (c);
}
waiters_.clear ();
}
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
> 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;
t->set_ref_count(2);
// allocate and spawn a child of t
tbb::empty_task * c = new (t.allocate_child()) tbb::empty_task;
t.spawn(c);
// do something else
// ...
t->wait_for_all();
And with TBB 2.2, we recommend even a simpler pattern to be used in most cases:
tbb::task_group tg;
tg.run( /*specify a function object here*/ );
// do something else
// ...
tg.wait();
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"tbb::empty_task * c = new (t.allocate_child()) tbb::empty_task; t.spawn(c);"
Er, spawning an empty task right after it was created isn't going to be terribly exciting...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Right, but I just used the same code as the author of the topic :)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
"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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page