- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
[cpp] while(executing_ != nullptr && executing_->state() == tbb::task::executing) executing_->wait_for_all(); // Can fail since ref_count could be decremented before calling [/cpp]
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
[cpp]class function_task_serializer { templateclass packaged_task : public tbb::task { public: packaged_task(boost::packaged_task && task, function_task_serializer& s) : task_(boost::move(task)), s_(s){} private: tbb::task* execute() { task_(); s_.note_completion(); return nullptr; } boost::packaged_task task_; function_task_serializer& s_; }; template class simple_task : public tbb::task { public: simple_task(Func&& func, function_task_serializer& s) : func_(std::move(func)), s_(s){} private: tbb::task* execute() { func_(); s_.note_completion(); return nullptr; } Func func_; function_task_serializer& s_; }; public: function_task_serializer() { count_ = 0; parent_ = new(tbb::task::allocate_root()) tbb::empty_task; parent_->set_ref_count(1); } void wait() { parent_->wait_for_all(); parent_->destroy(*parent_); } template void enqueue(Func&& func) { parent_->increment_ref_count(); queue_.push(new(parent_->allocate_child()) simple_task (std::forward (func), *this)); if(++count_ == 1) execute_item(); } template auto begin_invoke(Func&& func) -> boost::unique_future { auto task = boost::packaged_task (std::forward (func)); auto future = task.get_future(); parent_->increment_ref_count(); queue_.push(new(parent_->allocate_child()) packaged_task (boost::move(task), *this)); if(++count_ == 1) execute_item(); return std::move(future); } template auto invoke(Func&& func) -> decltype(func()) { return begin_invoke(std::forward (func)).get(); } private: void execute_item() { tbb::task* task; queue_.try_pop(task); tbb::task::enqueue(*task); } void note_completion() { if(--count_ > 0) execute_item(); } tbb::empty_task* parent_; tbb::concurrent_queue<:TASK> queue_; tbb::atomic count_; };[/cpp]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Unlike spawned tasks, you don't have to wait on enqueued tasks (this is why their other name is fire-and-forget tasks), but if you are willing to then you do it the same way as for spawned ones. I.e. task manipulation in your second implementation is correct.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It might also be a better idea to spawn instead of enqueue while waiting?
[cpp] void execute_item() { tbb::task* task; queue_.try_pop(task); if(!isWaiting_) tbb::task::enqueue(*task); else parent_->spawn(*task); // Execute as fast as possible while waiting }[/cpp]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Oh, if you call wait() in another task the you should only spawn(), not enqueue(). Due to FIFO-like execution, enqueued tasks are bad for nested parallelism, and currently there is a restriction that only a thread with no work on its stack can "dequeue" tasks (to prevent for risks of unbound stack growth and tasks blocked forever on the stack). Basically it means that waiting for enqueued tasks is only allowed for the main thread, otherwise deadlock can happen (and you seem to see it indeed happens).
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page