service_thread.cpp 3.6 KB

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