Short explanation in bold.
I have a tbb::task that can be summed up like that:
class UpdateTask
: public tbb::task
{
public:
tbb::task* execute()
{
// some work...
if( can_continue() )
{
// make sure this is re-executed
this->recycle_to_reexecute(); //looks like it is deprecated but it's more clear to me
return nullptr; // ? or this? or tbb::empty_task?
}
return nullptr;
}
};
I want this task to be rescheduled as soon as its finished until a condition is filled. I'm allocating this task this way:
UpdateTask& updater = *new(tbb::task::allocate_root()) UpdateTask();
Not sure it's related to the problem though.
The problem: When I run the code, I get assertions (in Debug) from tbb (last revision, 4.1.5) and I can't find a way to make it work correctly.
I've re-read the documentation but I can't find a simple example of this and it is not clear to me what I'm doing wrong. I made some experiments:
- With the code I just show, the assertion says that I should return a task:
Assertion t_next failed on line 485 of file ...\itbb\src\tbb\custom_scheduler.h Detailed description: reexecution requires that method execute() return another task
- Then if I return this, the assertions says that the task should be allocated:
Assertion t_next->state()==task::allocated failed on line 452 of file ...\itbb\src\tbb\custom_scheduler.h Detailed description: if task::execute() returns task, it must be marked as allo cated
- In doubt I tried to return a tbb::empty_task that I create on the fly (to check), allocated as
new(tbb::task::allocate_child()) tbb::empty_task()
. I got assertion with this messageAssertion p.ref_count==0 failed on line 107 of file ...\itbb\src\tbb\custom_scheduler.h Detailed description: completion of task caused predecessor's reference count to underflow
.
So, how to do it? I assume it is simple but can't find the way it is done. Is it related to task reference counting?
Update: here is a full code that is a good aproximation of what I have:
#include <iostream>
#include <atomic>
#include <tbb/tbb.h>
namespace test { class UpdateTask : public tbb::task { public:
UpdateTask() { std::cout << "[UpdateTask]" << std::endl; }
~UpdateTask() { std::cout << "\n[/UpdateTask]" << std::endl; }
tbb::task* execute() { std::cout << "Tick "<< m_count <<std::endl;
++m_count;
// make sure this is re-executed this->recycle_to_reexecute(); //return new(tbb::task::allocate_continuation()) tbb::empty_task(); return nullptr; }
private:
std::atomic<int> m_count;
};
tbb::task_list m_task_list;
void register_update_task() { UpdateTask& updater =
*new(tbb::task::allocate_root()) UpdateTask(); m_task_list.push_back( updater ); }
void run_and_wait() { tbb::task::spawn_root_and_wait( m_task_list ); }
void tbb_test() { register_update_task(); run_and_wait();
}
}
int main()
{
test::tbb_test();
std::cout << "\nEND";
std::cin.ignore();
return 0;
}
So, here I got the first exception saying that I should return a task. In execute()
function, if I replace the return by the one commented, then it appear to work, but there are two problems with this:
- I have to create an empty_task task EVERY TIME a call to execute is done???
- After the first call to execute(), the main thread is resumed. This is NOT what spawn_root_and_wait() is supposed to do. After all the task is not finished and is correctly rescheduled.
I must conclude it's not the right way to do this either.