- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
What I'm trying to do is create a task hierarchy by creating a task in one class that will eventually call tasks in another class. Initially I set every task to be a root task and then used "spawn_and_wait" which enabled me to pass a task_group_context around to cancel execution. As soon as I converted to only using one root along with continuation and child tasks, I'm consistently getting invalid task_group_context errors:
Module: tbb_debug.dll File: ../../src/tbb/scheduler.h Line: 458 Expression: !(ctx&~(3|7(<<task_group_context::traits_ofset))) task_group_context is invalid
I have tried both of the following scenarios. Scenario 1 is faster but neither works with a task_group_context created at the start.
Scenario 1: Two Root Tasks of Different Type ============================================ Class A * creates a task_group_context * spawns a task 1 (allocate_root) passing in context * execute method calls a function in Class B passing the same context Class B * spawns a task 2 (allocate_root) using the passed in context <== second root task * execute of task 2 spawns a task 3 as a continuation and task 4 as a child Scenario 2: Two Continuation Tasks of Different Type ===================================================== Class A * creates a task_group_context * spawns a task 1 (allocate_root) passing in context * execute method calls a function in Class B passing a "this" pointer Class B * spawns a task 2 (allocate_continuation) <== now a continuation of task 1 rather than a separate root * execute of task 2 spawns a task 3 also as a continuation and task 4 as a child
I suppose my questions boil down to the following:
- Is it wise to have multiple tasks allocated as root in a task hierarchy?
- If the approach is to only have one root task, what is the best way to spawn continuation and child tasks in a different class that shares the same task_group_context?
Any guidance is appreciated. Thanks.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Okay... I've stumbled on a solution but I'm not entirely sure why it works. A tbb::task_group_context is never explicitly created. Instead, the implicit task_group_context of the initial root task in Class A is passed to the second root task in Class B. That root task then uses it when allocating itself as a root task.
ClassA: auto fn = std::bind(&ClassA::initialize, this, std::placeholders_1); Task1* t1 = new(tbb::allocate_root()) Task1(fn) tbb::task::spawn(*t1) In the execute() method of Task1: tbb::task* execute() { _fn(this->group); <== pass the implicit context? to Class B return nullptr; } Class B: Task2* t2 = new(tbb::allocate_root(*context)) Task2(); <== context passed in from Class A tbb::task::spawn(*t2); In the execute() method of Task 2: tbb::task* execute() { Task3* t3 = new(tbb::task::allocate_continuation()) Task3(); Task4* t4 = new(t3->allocate_child()) Task4(); t3->set_ref_count(1); t3->spawn(*t4); return nullptr; }
Task 3 and 4 and now able to respond to cancellation requests via this->group().
However, if I derive from tbb::task_group_context to create a CustomContext, I need to set it in Class A Task1 when allocating root. In that case, I can't seem to get the context over to Class B.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Things seem to be working with the following code:
class taskGroupContext : public tbb::task_group_context { public: taskGroupContext() { _paused = false; } ~taskGroupContext() { } bool is_group_execution_paused() const { return _paused; } void pause_group_execution(const bool pause) { _paused = pause; } private: tbb::atomic<bool> _paused; }; void ClassA::TASK_createTask1(Message msg) { auto context = new taskGroupContext(); auto fn = std::bind(&ClassA::initialize, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6); Task1* t = new(tbb::task::allocate_root(*context)) Task1(fn, msg, context); _tasks.push_back(t); tbb::task::spawn(*t); } //Task1's execute method tbb::task* execute() { _fnc(_msg.p1, _msg.p2, _msg.p3, _msg.p4, _msg.p5, _context); _finished = true; return nullptr; } bool ClassA::initialize(int p1, int p2, int p3, int p4, int p5, taskGroupContext* context) { //do some additional work return _classB_instance->getStuff(p1, p2, context); } bool ClassB::getStuff(int p1, int p2, taskGroupContext* context) { auto callback = std::bind(&ClassB::onDataAvailable, this, std::placeholders::_1, std::placeholders::_2); GetDataTask* t = new(tbb::task::allocate_root(*context)) GetDataTask(p1, callback, context); tbb::task::spawn(*t); return true; } //GetDataTask's execute method tbb::task* execute() { BroadcastDataTask* bt = new(tbb::task::allocate_continuation()) BroadcastDataTask(_p1, _container1, _container2, _callback, _context); GenerateRandomDataTask* gt = new(bt->allocate_child()) GenerateRandomDataTask(_container1, _container2, _context); bt->set_ref_count(1); bt->spawn(*gt); return nullptr; } //Then in both the continuation and child task, I have code as follows: if (_context->is_group_execution_cancelled()) { _context->reset(); return nullptr; } while (_context->is_group_execution_paused()) { if (_context->is_group_execution_cancelled()) { _context->reset(); return nullptr; } }
I'm am sharing the derived task_group_context between two root tasks and then passing a pointer to that context to the continuation and child tasks. Things appear to be working as I am able to pause and cancel tasks on demand.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
While the above code works for cancellation using a CustomContext derived from tbb::task_group_context, there is a memory leak on Line 23: auto context =
new
taskGroupContext()
is never actually deallocated.
To manage this, I have created a TaskManager class that owns a derived context as unique_ptr in a vector. The entire task group has a raw pointer to this context. When the innermost child task is finished, a flag is set. When this flag is set, the TaskManager knows it is safe to remove the context from the vector.
However, by doing so, I'm running into the same error:
Module: tbb_debug.dll File: ../../src/tbb/scheduler.h Line: 458 Expression: !(ctx&~(3|7(<<task_group_context::traits_ofset))) task_group_context is invalid
I do not get much of a callstack from which to debug. I see all of my task destructors getting hit, the CustomContext destructor getting hit, but eventually I get the assertion being thrown.
Since I'm using a derived class for the context, I'm guessing the base context is already cleaned up somewhere by tbb? When I remove my derived context from the container, its destructor gets hit (which is fine) but then it tries to deallocate the base context?
Not quite sure what is going on... Any suggestions on cleaning up a derived task_group_context is appreciated.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page