thread_worker.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <atomic>
  5. #include <condition_variable>
  6. #include <functional>
  7. #include <mutex>
  8. #include <string>
  9. #include <thread>
  10. #include <type_traits>
  11. #include <vector>
  12. #include <queue>
  13. #include "common/polyfill_thread.h"
  14. #include "common/thread.h"
  15. #include "common/unique_function.h"
  16. namespace Common {
  17. template <class StateType = void>
  18. class StatefulThreadWorker {
  19. static constexpr bool with_state = !std::is_same_v<StateType, void>;
  20. struct DummyCallable {
  21. int operator()() const noexcept {
  22. return 0;
  23. }
  24. };
  25. using Task =
  26. std::conditional_t<with_state, UniqueFunction<void, StateType*>, UniqueFunction<void>>;
  27. using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
  28. public:
  29. explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
  30. : workers_queued{num_workers}, thread_name{std::move(name)} {
  31. const auto lambda = [this, func](std::stop_token stop_token) {
  32. Common::SetCurrentThreadName(thread_name.c_str());
  33. {
  34. [[maybe_unused]] std::conditional_t<with_state, StateType, int> state{func()};
  35. while (!stop_token.stop_requested()) {
  36. Task task;
  37. {
  38. std::unique_lock lock{queue_mutex};
  39. if (requests.empty()) {
  40. wait_condition.notify_all();
  41. }
  42. Common::CondvarWait(condition, lock, stop_token,
  43. [this] { return !requests.empty(); });
  44. if (stop_token.stop_requested()) {
  45. break;
  46. }
  47. task = std::move(requests.front());
  48. requests.pop();
  49. }
  50. if constexpr (with_state) {
  51. task(&state);
  52. } else {
  53. task();
  54. }
  55. ++work_done;
  56. }
  57. }
  58. ++workers_stopped;
  59. wait_condition.notify_all();
  60. };
  61. threads.reserve(num_workers);
  62. for (size_t i = 0; i < num_workers; ++i) {
  63. threads.emplace_back(lambda);
  64. }
  65. }
  66. StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete;
  67. StatefulThreadWorker(const StatefulThreadWorker&) = delete;
  68. StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete;
  69. StatefulThreadWorker(StatefulThreadWorker&&) = delete;
  70. void QueueWork(Task work) {
  71. {
  72. std::unique_lock lock{queue_mutex};
  73. requests.emplace(std::move(work));
  74. ++work_scheduled;
  75. }
  76. condition.notify_one();
  77. }
  78. void WaitForRequests(std::stop_token stop_token = {}) {
  79. std::stop_callback callback(stop_token, [this] {
  80. for (auto& thread : threads) {
  81. thread.request_stop();
  82. }
  83. });
  84. std::unique_lock lock{queue_mutex};
  85. wait_condition.wait(lock, [this] {
  86. return workers_stopped >= workers_queued || work_done >= work_scheduled;
  87. });
  88. }
  89. private:
  90. std::queue<Task> requests;
  91. std::mutex queue_mutex;
  92. std::condition_variable_any condition;
  93. std::condition_variable wait_condition;
  94. std::atomic<size_t> work_scheduled{};
  95. std::atomic<size_t> work_done{};
  96. std::atomic<size_t> workers_stopped{};
  97. std::atomic<size_t> workers_queued{};
  98. std::string thread_name;
  99. std::vector<std::jthread> threads;
  100. };
  101. using ThreadWorker = StatefulThreadWorker<>;
  102. } // namespace Common