service_thread.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <condition_variable>
  4. #include <functional>
  5. #include <mutex>
  6. #include <thread>
  7. #include <vector>
  8. #include <queue>
  9. #include "common/scope_exit.h"
  10. #include "common/thread.h"
  11. #include "core/hle/kernel/k_session.h"
  12. #include "core/hle/kernel/k_thread.h"
  13. #include "core/hle/kernel/kernel.h"
  14. #include "core/hle/kernel/service_thread.h"
  15. namespace Kernel {
  16. class ServiceThread::Impl final {
  17. public:
  18. explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
  19. ~Impl();
  20. void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
  21. private:
  22. std::vector<std::jthread> threads;
  23. std::queue<std::function<void()>> requests;
  24. std::mutex queue_mutex;
  25. std::condition_variable_any condition;
  26. const std::string service_name;
  27. };
  28. ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
  29. : service_name{name} {
  30. for (std::size_t i = 0; i < num_threads; ++i) {
  31. threads.emplace_back([this, &kernel](std::stop_token stop_token) {
  32. Common::SetCurrentThreadName(std::string{service_name}.c_str());
  33. // Wait for first request before trying to acquire a render context
  34. {
  35. std::unique_lock lock{queue_mutex};
  36. condition.wait(lock, stop_token, [this] { return !requests.empty(); });
  37. }
  38. if (stop_token.stop_requested()) {
  39. return;
  40. }
  41. // Allocate a dummy guest thread for this host thread.
  42. kernel.RegisterHostThread();
  43. while (true) {
  44. std::function<void()> task;
  45. {
  46. std::unique_lock lock{queue_mutex};
  47. condition.wait(lock, stop_token, [this] { return !requests.empty(); });
  48. if (stop_token.stop_requested()) {
  49. return;
  50. }
  51. if (requests.empty()) {
  52. continue;
  53. }
  54. task = std::move(requests.front());
  55. requests.pop();
  56. }
  57. task();
  58. }
  59. });
  60. }
  61. }
  62. void ServiceThread::Impl::QueueSyncRequest(KSession& session,
  63. std::shared_ptr<HLERequestContext>&& context) {
  64. {
  65. std::unique_lock lock{queue_mutex};
  66. auto* server_session{&session.GetServerSession()};
  67. // Open a reference to the session to ensure it is not closes while the service request
  68. // completes asynchronously.
  69. server_session->Open();
  70. requests.emplace([server_session, context{std::move(context)}]() {
  71. // Close the reference.
  72. SCOPE_EXIT({ server_session->Close(); });
  73. // Complete the service request.
  74. server_session->CompleteSyncRequest(*context);
  75. });
  76. }
  77. condition.notify_one();
  78. }
  79. ServiceThread::Impl::~Impl() {
  80. condition.notify_all();
  81. for (auto& thread : threads) {
  82. thread.request_stop();
  83. thread.join();
  84. }
  85. }
  86. ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
  87. : impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
  88. ServiceThread::~ServiceThread() = default;
  89. void ServiceThread::QueueSyncRequest(KSession& session,
  90. std::shared_ptr<HLERequestContext>&& context) {
  91. impl->QueueSyncRequest(session, std::move(context));
  92. }
  93. } // namespace Kernel