AsyncRunner.h 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. /*
  2. * AsyncRunner.h, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #pragma once
  11. #include <tbb/task_arena.h>
  12. #include <tbb/task_group.h>
  13. #include <tbb/global_control.h>
  14. VCMI_LIB_NAMESPACE_BEGIN
  15. /// Helper class for running asynchronous tasks using TBB thread pool
  16. class AsyncRunner : boost::noncopyable
  17. {
  18. tbb::task_arena arena;
  19. tbb::task_group taskGroup;
  20. tbb::global_control control;
  21. static int selectArenaSize()
  22. {
  23. // WARNING:
  24. // Due to blocking waits in AI logic, we require some oversubscription on system with small number of cores
  25. // othervice, it is possible for AI threads to stuck in blocking wait, while task that will unblock AI never assigned to worker thread
  26. // TBB provides resumable tasks support, however this support is not available on all systems (most notably - Android)
  27. // To work around this problem, request TBB to create few extra threads when running on CPU with low max concurrency
  28. // Issue confirmed to happen (but likely not limited to) on iPhone 12 Pro (2+4 cores) and on virtual systems with 2 cores
  29. int cores = tbb::this_task_arena::max_concurrency();
  30. int requiredWorkersCount = 4;
  31. return std::max(cores, requiredWorkersCount);
  32. }
  33. public:
  34. AsyncRunner()
  35. :arena(selectArenaSize())
  36. ,control(tbb::global_control::max_allowed_parallelism, selectArenaSize())
  37. {}
  38. /// Runs the provided functor asynchronously on a thread from the TBB worker pool.
  39. template<typename Functor>
  40. void run(Functor && f)
  41. {
  42. arena.enqueue(taskGroup.defer(std::forward<Functor>(f)));
  43. }
  44. /// Waits for all previously enqueued task.
  45. /// Re-entrable - waiting for tasks does not prevent submitting new tasks
  46. void wait()
  47. {
  48. taskGroup.wait();
  49. }
  50. ~AsyncRunner()
  51. {
  52. wait();
  53. }
  54. };
  55. VCMI_LIB_NAMESPACE_END