bshoshany / thread-pool

BS::thread_pool: a fast, lightweight, and easy-to-use C++17 thread pool library
MIT License
2.21k stars 253 forks source link

[REQ] does this library support recursive/nested tasks? #142

Closed wkjarosz closed 8 months ago

wkjarosz commented 8 months ago

I did not see this mentioned in the documentation (sorry if I missed it).

Does this library support nested or recursive tasks without deadlock/thread starvation?

For instance, I need to perform 10 tasks, which can be done independently, but within each tasks, I might have several loops that I would like to parallelize as well. If I use submit_loop or similar within each of these 10 tasks, do I run the risk of the inner loops never making progress because all threads in the pool are already allocated to the outer set of 10 tasks?

My understanding is that with this library, the calling thread never steps in to make progress on a task or loop if the threadpool is full, which means deadlock could indeed happen with the above type of workload structure.

Thanks

bshoshany commented 8 months ago

Detaching tasks using detach_X() is always safe to do from within another task, because that won't block, as long as you don't use pool.wait(). However, using submit_X() and waiting for the submitted tasks may cause a deadlock, since the waiting task will block until all the waited for tasks are done, and if the number of waiting tasks is equal to the total number of available threads, the waited for tasks will never get to execute.

To be safe, I recommend either submitting all tasks from the main thread, or creating two thread pools where one pool's tasks only submit tasks for the other pool.

wkjarosz commented 8 months ago

Thanks for the info and suggested workarounds. Unfortunately, this probably means BS::thread_pool is not the right design for my needs, so i'll likely go in a different direction. Thanks again.

bshoshany commented 8 months ago

No worries, but I don't think any other thread pool library will allow you to do what you want, because the very definition of a thread pool means one task cannot wait for other tasks in the same pool. You might want to look into C++20 coroutines or other methods of asynchronous executing instead.

wkjarosz commented 8 months ago

There are threadpool designs that can avoid issues with recursive tasks or one task depending on the completion of another in the same pool. You can check out the following two resources:

https://maxliani.wordpress.com/2022/07/27/anatomy-of-a-task-scheduler/ https://github.com/mitsuba-renderer/nanothread

One way to enable this is to ensure that wait-type functions don't just wait, but have the calling thread step into and execute tasks from the thread pool (incidentally, this also allows the thread pool to have a size of zero where no additional threads are ever launched).

bshoshany commented 8 months ago

Thanks, I will take a look.