yuzu/src/core/hle/kernel/service_thread.cpp
Morph 99ceb03a1c general: Convert source file copyright comments over to SPDX
This formats all copyright comments according to SPDX formatting guidelines.
Additionally, this resolves the remaining GPLv2 only licensed files by relicensing them to GPLv2.0-or-later.
2022-04-23 05:55:32 -04:00

120 lines
3.6 KiB
C++

// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
#include <queue>
#include "common/scope_exit.h"
#include "common/thread.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/service_thread.h"
namespace Kernel {
class ServiceThread::Impl final {
public:
explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
~Impl();
void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
private:
std::vector<std::jthread> threads;
std::queue<std::function<void()>> requests;
std::mutex queue_mutex;
std::condition_variable_any condition;
const std::string service_name;
};
ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
: service_name{name} {
for (std::size_t i = 0; i < num_threads; ++i) {
threads.emplace_back([this, &kernel](std::stop_token stop_token) {
Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
// Wait for first request before trying to acquire a render context
{
std::unique_lock lock{queue_mutex};
condition.wait(lock, stop_token, [this] { return !requests.empty(); });
}
if (stop_token.stop_requested()) {
return;
}
// Allocate a dummy guest thread for this host thread.
kernel.RegisterHostThread();
while (true) {
std::function<void()> task;
{
std::unique_lock lock{queue_mutex};
condition.wait(lock, stop_token, [this] { return !requests.empty(); });
if (stop_token.stop_requested()) {
return;
}
if (requests.empty()) {
continue;
}
task = std::move(requests.front());
requests.pop();
}
task();
}
});
}
}
void ServiceThread::Impl::QueueSyncRequest(KSession& session,
std::shared_ptr<HLERequestContext>&& context) {
{
std::unique_lock lock{queue_mutex};
auto* server_session{&session.GetServerSession()};
// Open a reference to the session to ensure it is not closes while the service request
// completes asynchronously.
server_session->Open();
requests.emplace([server_session, context{std::move(context)}]() {
// Close the reference.
SCOPE_EXIT({ server_session->Close(); });
// Complete the service request.
server_session->CompleteSyncRequest(*context);
});
}
condition.notify_one();
}
ServiceThread::Impl::~Impl() {
condition.notify_all();
for (auto& thread : threads) {
thread.request_stop();
thread.join();
}
}
ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
: impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
ServiceThread::~ServiceThread() = default;
void ServiceThread::QueueSyncRequest(KSession& session,
std::shared_ptr<HLERequestContext>&& context) {
impl->QueueSyncRequest(session, std::move(context));
}
} // namespace Kernel