thread_worker.h 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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 <stop_token>
  9. #include <string>
  10. #include <thread>
  11. #include <type_traits>
  12. #include <vector>
  13. #include <queue>
  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. condition.wait(lock, stop_token, [this] { return !requests.empty(); });
  43. if (stop_token.stop_requested()) {
  44. break;
  45. }
  46. task = std::move(requests.front());
  47. requests.pop();
  48. }
  49. if constexpr (with_state) {
  50. task(&state);
  51. } else {
  52. task();
  53. }
  54. ++work_done;
  55. }
  56. }
  57. ++workers_stopped;
  58. wait_condition.notify_all();
  59. };
  60. threads.reserve(num_workers);
  61. for (size_t i = 0; i < num_workers; ++i) {
  62. threads.emplace_back(lambda);
  63. }
  64. }
  65. StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete;
  66. StatefulThreadWorker(const StatefulThreadWorker&) = delete;
  67. StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete;
  68. StatefulThreadWorker(StatefulThreadWorker&&) = delete;
  69. void QueueWork(Task work) {
  70. {
  71. std::unique_lock lock{queue_mutex};
  72. requests.emplace(std::move(work));
  73. ++work_scheduled;
  74. }
  75. condition.notify_one();
  76. }
  77. void WaitForRequests(std::stop_token stop_token = {}) {
  78. std::stop_callback callback(stop_token, [this] {
  79. for (auto& thread : threads) {
  80. thread.request_stop();
  81. }
  82. });
  83. std::unique_lock lock{queue_mutex};
  84. wait_condition.wait(lock, [this] {
  85. return workers_stopped >= workers_queued || work_done >= work_scheduled;
  86. });
  87. }
  88. private:
  89. std::queue<Task> requests;
  90. std::mutex queue_mutex;
  91. std::condition_variable_any condition;
  92. std::condition_variable wait_condition;
  93. std::atomic<size_t> work_scheduled{};
  94. std::atomic<size_t> work_done{};
  95. std::atomic<size_t> workers_stopped{};
  96. std::atomic<size_t> workers_queued{};
  97. std::string thread_name;
  98. std::vector<std::jthread> threads;
  99. };
  100. using ThreadWorker = StatefulThreadWorker<>;
  101. } // namespace Common