From 983f2b70741f17f30fe2321451f10cabecc013d2 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 16 Oct 2022 01:53:56 -0400 Subject: kernel: invert session request handling flow --- src/core/hle/kernel/hle_ipc.cpp | 55 +++----- src/core/hle/kernel/hle_ipc.h | 29 ++-- src/core/hle/kernel/k_client_port.cpp | 5 +- src/core/hle/kernel/k_client_port.h | 3 +- src/core/hle/kernel/k_port.cpp | 6 - src/core/hle/kernel/k_server_port.cpp | 5 +- src/core/hle/kernel/k_server_port.h | 19 --- src/core/hle/kernel/k_server_session.cpp | 187 +++++++++++++++++-------- src/core/hle/kernel/k_server_session.h | 37 +---- src/core/hle/kernel/k_session.cpp | 5 +- src/core/hle/kernel/k_session.h | 3 +- src/core/hle/kernel/kernel.cpp | 24 +++- src/core/hle/kernel/kernel.h | 9 ++ src/core/hle/kernel/service_thread.cpp | 232 ++++++++++++++++++++++--------- src/core/hle/kernel/service_thread.h | 6 +- src/core/hle/kernel/svc.cpp | 6 +- 16 files changed, 381 insertions(+), 250 deletions(-) (limited to 'src/core/hle/kernel') diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index e4f43a053..fd354d484 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -35,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se } SessionRequestHandler::~SessionRequestHandler() { - kernel.ReleaseServiceThread(service_thread); + kernel.ReleaseServiceThread(service_thread.lock()); +} + +void SessionRequestHandler::AcceptSession(KServerPort* server_port) { + auto* server_session = server_port->AcceptSession(); + ASSERT(server_session != nullptr); + + RegisterSession(server_session, std::make_shared(kernel)); +} + +void SessionRequestHandler::RegisterSession(KServerSession* server_session, + std::shared_ptr manager) { + manager->SetSessionHandler(shared_from_this()); + service_thread.lock()->RegisterServerSession(server_session, manager); + server_session->Close(); } SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {} @@ -92,7 +107,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses } // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs - context.SetSessionRequestManager(server_session->GetSessionRequestManager()); + ASSERT(context.GetManager().get() == this); // If there is a DomainMessageHeader, then this is CommandType "Request" const auto& domain_message_header = context.GetDomainMessageHeader(); @@ -130,31 +145,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses return ResultSuccess; } -Result SessionRequestManager::QueueSyncRequest(KSession* parent, - std::shared_ptr&& context) { - // Ensure we have a session request handler - if (this->HasSessionRequestHandler(*context)) { - if (auto strong_ptr = this->GetServiceThread().lock()) { - strong_ptr->QueueSyncRequest(*parent, std::move(context)); - } else { - ASSERT_MSG(false, "strong_ptr is nullptr!"); - } - } else { - ASSERT_MSG(false, "handler is invalid!"); - } - - return ResultSuccess; -} - -void SessionRequestHandler::ClientConnected(KServerSession* session) { - session->GetSessionRequestManager()->SetSessionHandler(shared_from_this()); - - // Ensure our server session is tracked globally. - kernel.RegisterServerObject(session); -} - -void SessionRequestHandler::ClientDisconnected(KServerSession* session) {} - HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, KServerSession* server_session_, KThread* thread_) : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} { @@ -214,7 +204,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 // Padding to align to 16 bytes rp.AlignWithPadding(); - if (Session()->GetSessionRequestManager()->IsDomain() && + if (GetManager()->IsDomain() && ((command_header->type == IPC::CommandType::Request || command_header->type == IPC::CommandType::RequestWithContext) || !incoming)) { @@ -223,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 if (incoming || domain_message_header) { domain_message_header = rp.PopRaw(); } else { - if (Session()->GetSessionRequestManager()->IsDomain()) { + if (GetManager()->IsDomain()) { LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); } } @@ -316,12 +306,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. - if (server_session->GetSessionRequestManager()->IsDomain()) { + if (GetManager()->IsDomain()) { current_offset = domain_offset - static_cast(outgoing_domain_objects.size()); for (auto& object : outgoing_domain_objects) { - server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object)); - cmd_buf[current_offset++] = static_cast( - server_session->GetSessionRequestManager()->DomainHandlerCount()); + GetManager()->AppendDomainHandler(std::move(object)); + cmd_buf[current_offset++] = static_cast(GetManager()->DomainHandlerCount()); } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 1083638a9..67da8e7e1 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -45,11 +45,13 @@ class KAutoObject; class KernelCore; class KEvent; class KHandleTable; +class KServerPort; class KProcess; class KServerSession; class KThread; class KReadableEvent; class KSession; +class SessionRequestManager; class ServiceThread; enum class ThreadWakeupReason; @@ -76,19 +78,9 @@ public: virtual Result HandleSyncRequest(Kernel::KServerSession& session, Kernel::HLERequestContext& context) = 0; - /** - * Signals that a client has just connected to this HLE handler and keeps the - * associated ServerSession alive for the duration of the connection. - * @param server_session Owning pointer to the ServerSession associated with the connection. - */ - void ClientConnected(KServerSession* session); - - /** - * Signals that a client has just disconnected from this HLE handler and releases the - * associated ServerSession. - * @param server_session ServerSession associated with the connection. - */ - void ClientDisconnected(KServerSession* session); + void AcceptSession(KServerPort* server_port); + void RegisterSession(KServerSession* server_session, + std::shared_ptr manager); std::weak_ptr GetServiceThread() const { return service_thread; @@ -170,7 +162,6 @@ public: Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context); Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context); - Result QueueSyncRequest(KSession* parent, std::shared_ptr&& context); private: bool convert_to_domain{}; @@ -350,11 +341,11 @@ public: template std::shared_ptr GetDomainHandler(std::size_t index) const { - return std::static_pointer_cast(manager.lock()->DomainHandler(index).lock()); + return std::static_pointer_cast(GetManager()->DomainHandler(index).lock()); } void SetSessionRequestManager(std::weak_ptr manager_) { - manager = std::move(manager_); + manager = manager_; } std::string Description() const; @@ -363,6 +354,10 @@ public: return *thread; } + std::shared_ptr GetManager() const { + return manager.lock(); + } + private: friend class IPC::ResponseBuilder; @@ -396,7 +391,7 @@ private: u32 handles_offset{}; u32 domain_offset{}; - std::weak_ptr manager; + std::weak_ptr manager{}; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 3cb22ff4d..eaa2e094c 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -58,8 +58,7 @@ bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } -Result KClientPort::CreateSession(KClientSession** out, - std::shared_ptr session_manager) { +Result KClientPort::CreateSession(KClientSession** out) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), LimitableResource::Sessions); @@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out, } // Initialize the session. - session->Initialize(this, parent->GetName(), session_manager); + session->Initialize(this, parent->GetName()); // Commit the session reservation. session_reservation.Commit(); diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index e17eff28f..81046fb86 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -52,8 +52,7 @@ public: void Destroy() override; bool IsSignaled() const override; - Result CreateSession(KClientSession** out, - std::shared_ptr session_manager = nullptr); + Result CreateSession(KClientSession** out); private: std::atomic num_sessions{}; diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp index 7a5a9dc2a..77d00ae2c 100644 --- a/src/core/hle/kernel/k_port.cpp +++ b/src/core/hle/kernel/k_port.cpp @@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) { server.EnqueueSession(session); - if (auto session_ptr = server.GetSessionRequestHandler().lock()) { - session_ptr->ClientConnected(server.AcceptSession()); - } else { - ASSERT(false); - } - return ResultSuccess; } diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index e968f26ad..12e0c3ffb 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -19,6 +19,8 @@ void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) { // Set member variables. parent = parent_port_; name = std::move(name_); + + kernel.RegisterServerObject(this); } bool KServerPort::IsLight() const { @@ -62,9 +64,6 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); - // Release host emulation members. - session_handler.reset(); - // Ensure that the global list tracking server objects does not hold on to a reference. kernel.UnregisterServerObject(this); } diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index fd4f4bd20..5fc7ee683 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -27,24 +27,6 @@ public: void Initialize(KPort* parent_port_, std::string&& name_); - /// Whether or not this server port has an HLE handler available. - bool HasSessionRequestHandler() const { - return !session_handler.expired(); - } - - /// Gets the HLE handler for this port. - SessionRequestHandlerWeakPtr GetSessionRequestHandler() const { - return session_handler; - } - - /** - * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port - * will inherit a reference to this handler. - */ - void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) { - session_handler = std::move(handler); - } - void EnqueueSession(KServerSession* pending_session); KServerSession* AcceptSession(); @@ -65,7 +47,6 @@ private: void CleanupSessions(); SessionList session_list; - SessionRequestHandlerWeakPtr session_handler; KPort* parent{}; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index faf03fcc8..aa1941f01 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_) KServerSession::~KServerSession() = default; -void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr manager_) { +void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) { // Set member variables. parent = parent_session_; name = std::move(name_); - manager = manager_; } void KServerSession::Destroy() { @@ -47,18 +45,99 @@ void KServerSession::Destroy() { this->CleanupRequests(); parent->Close(); - - // Release host emulation members. - manager.reset(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } void KServerSession::OnClientClosed() { - if (manager && manager->HasSessionHandler()) { - manager->SessionHandler().ClientDisconnected(this); + KScopedLightLock lk{m_lock}; + + // Handle any pending requests. + KSessionRequest* prev_request = nullptr; + while (true) { + // Declare variables for processing the request. + KSessionRequest* request = nullptr; + KEvent* event = nullptr; + KThread* thread = nullptr; + bool cur_request = false; + bool terminate = false; + + // Get the next request. + { + KScopedSchedulerLock sl{kernel}; + + if (m_current_request != nullptr && m_current_request != prev_request) { + // Set the request, open a reference as we process it. + request = m_current_request; + request->Open(); + cur_request = true; + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + + // If the thread is terminating, handle that. + if (thread->IsTerminationRequested()) { + request->ClearThread(); + request->ClearEvent(); + terminate = true; + } + + prev_request = request; + } else if (!m_request_list.empty()) { + // Pop the request from the front of the list. + request = std::addressof(m_request_list.front()); + m_request_list.pop_front(); + + // Get thread and event for the request. + thread = request->GetThread(); + event = request->GetEvent(); + } + } + + // If there are no requests, we're done. + if (request == nullptr) { + break; + } + + // All requests must have threads. + ASSERT(thread != nullptr); + + // Ensure that we close the request when done. + SCOPE_EXIT({ request->Close(); }); + + // If we're terminating, close a reference to the thread and event. + if (terminate) { + thread->Close(); + if (event != nullptr) { + event->Close(); + } + } + + // If we need to, reply. + if (event != nullptr && !cur_request) { + // There must be no mappings. + ASSERT(request->GetSendCount() == 0); + ASSERT(request->GetReceiveCount() == 0); + ASSERT(request->GetExchangeCount() == 0); + + // // Get the process and page table. + // KProcess *client_process = thread->GetOwnerProcess(); + // auto &client_pt = client_process->GetPageTable(); + + // // Reply to the request. + // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(), + // ResultSessionClosed); + + // // Unlock the buffer. + // // NOTE: Nintendo does not check the result of this. + // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize()); + + // Signal the event. + event->Signal(); + } } + + // Notify. + this->NotifyAvailable(ResultSessionClosed); } bool KServerSession::IsSignaled() const { @@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const { return !m_request_list.empty() && m_current_request == nullptr; } -Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { - u32* cmd_buf{reinterpret_cast(memory.GetPointer(thread->GetTLSAddress()))}; - auto context = std::make_shared(kernel, memory, this, thread); - - context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); - - return manager->QueueSyncRequest(parent, std::move(context)); -} - -Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { - Result result = manager->CompleteSyncRequest(this, context); - - // The calling thread is waiting for this request to complete, so wake it up. - context.GetThread().EndWait(result); - - return result; -} - Result KServerSession::OnRequest(KSessionRequest* request) { // Create the wait queue. ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; @@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) { // Check that we're not terminating. R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested); - if (manager) { - // HLE request. - auto& memory{kernel.System().Memory()}; - this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); - } else { - // Non-HLE request. - - // Get whether we're empty. - const bool was_empty = m_request_list.empty(); + // Get whether we're empty. + const bool was_empty = m_request_list.empty(); - // Add the request to the list. - request->Open(); - m_request_list.push_back(*request); + // Add the request to the list. + request->Open(); + m_request_list.push_back(*request); - // If we were empty, signal. - if (was_empty) { - this->NotifyAvailable(); - } + // If we were empty, signal. + if (was_empty) { + this->NotifyAvailable(); } // If we have a request event, this is asynchronous, and we don't need to wait. @@ -136,7 +189,7 @@ Result KServerSession::OnRequest(KSessionRequest* request) { return GetCurrentThread(kernel).GetWaitResult(); } -Result KServerSession::SendReply() { +Result KServerSession::SendReply(bool is_hle) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -171,13 +224,18 @@ Result KServerSession::SendReply() { Result result = ResultSuccess; if (!closed) { // If we're not closed, send the reply. - Core::Memory::Memory& memory{kernel.System().Memory()}; - KThread* server_thread{GetCurrentThreadPointer(kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + if (is_hle) { + // HLE servers write directly to a pointer to the thread command buffer. Therefore + // the reply has already been written in this case. + } else { + Core::Memory::Memory& memory{kernel.System().Memory()}; + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - auto* dst_msg_buffer = memory.GetPointer(client_message); - std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + auto* dst_msg_buffer = memory.GetPointer(client_message); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } } else { result = ResultSessionClosed; } @@ -223,7 +281,8 @@ Result KServerSession::SendReply() { return result; } -Result KServerSession::ReceiveRequest() { +Result KServerSession::ReceiveRequest(std::shared_ptr* out_context, + std::weak_ptr manager) { // Lock the session. KScopedLightLock lk{m_lock}; @@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() { // Receive the message. Core::Memory::Memory& memory{kernel.System().Memory()}; - KThread* server_thread{GetCurrentThreadPointer(kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + if (out_context != nullptr) { + // HLE request. + u32* cmd_buf{reinterpret_cast(memory.GetPointer(client_message))}; + *out_context = std::make_shared(kernel, memory, this, client_thread); + (*out_context)->SetSessionRequestManager(manager); + (*out_context) + ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), + cmd_buf); + } else { + KThread* server_thread{GetCurrentThreadPointer(kernel)}; + UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(client_message); - auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); - std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + auto* src_msg_buffer = memory.GetPointer(client_message); + auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); + std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + } // We succeeded. return ResultSuccess; diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index 188aef4af..e4698d3f5 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -16,21 +16,11 @@ #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/result.h" -namespace Core::Memory { -class Memory; -} - -namespace Core::Timing { -class CoreTiming; -struct EventType; -} // namespace Core::Timing - namespace Kernel { class HLERequestContext; class KernelCore; class KSession; -class SessionRequestHandler; class SessionRequestManager; class KThread; @@ -46,8 +36,7 @@ public: void Destroy() override; - void Initialize(KSession* parent_session_, std::string&& name_, - std::shared_ptr manager_); + void Initialize(KSession* parent_session_, std::string&& name_); KSession* GetParent() { return parent; @@ -60,32 +49,16 @@ public: bool IsSignaled() const override; void OnClientClosed(); - /// Gets the session request manager, which forwards requests to the underlying service - std::shared_ptr& GetSessionRequestManager() { - return manager; - } - /// TODO: flesh these out to match the real kernel Result OnRequest(KSessionRequest* request); - Result SendReply(); - Result ReceiveRequest(); + Result SendReply(bool is_hle = false); + Result ReceiveRequest(std::shared_ptr* out_context = nullptr, + std::weak_ptr manager = {}); private: /// Frees up waiting client sessions when this server session is about to die void CleanupRequests(); - /// Queues a sync request from the emulated application. - Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory); - - /// Completes a sync request from the emulated application. - Result CompleteSyncRequest(HLERequestContext& context); - - /// This session's HLE request handlers; if nullptr, this is not an HLE server - std::shared_ptr manager; - - /// When set to True, converts the session to a domain at the end of the command - bool convert_to_domain{}; - /// KSession that owns this KServerSession KSession* parent{}; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index ee05aa282..7a6534ac3 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {} KSession::~KSession() = default; -void KSession::Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr manager_) { +void KSession::Initialize(KClientPort* port_, const std::string& name_) { // Increment reference count. // Because reference count is one on creation, this will result // in a reference count of two. Thus, when both server and client are closed @@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_, KAutoObject::Create(std::addressof(client)); // Initialize our sub sessions. - server.Initialize(this, name_ + ":Server", manager_); + server.Initialize(this, name_ + ":Server"); client.Initialize(this, name_ + ":Client"); // Set state and name. diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index c6ead403b..93e5e6f71 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -21,8 +21,7 @@ public: explicit KSession(KernelCore& kernel_); ~KSession() override; - void Initialize(KClientPort* port_, const std::string& name_, - std::shared_ptr manager_ = nullptr); + void Initialize(KClientPort* port_, const std::string& name_); void Finalize() override; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index fdc774e30..29e122dfd 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -60,7 +60,6 @@ struct KernelCore::Impl { global_scheduler_context = std::make_unique(kernel); global_handle_table = std::make_unique(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); - default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; @@ -86,6 +85,8 @@ struct KernelCore::Impl { } RegisterHostThread(); + + default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); } void InitializeCores() { @@ -703,6 +704,15 @@ struct KernelCore::Impl { return port; } + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + auto search = service_interface_handlers.find(name); + if (search == service_interface_handlers.end()) { + return; + } + + search->second(system.ServiceManager(), server_port); + } + void RegisterServerObject(KAutoObject* server_object) { std::scoped_lock lk(server_objects_lock); server_objects.insert(server_object); @@ -715,7 +725,7 @@ struct KernelCore::Impl { std::weak_ptr CreateServiceThread(KernelCore& kernel, const std::string& name) { - auto service_thread = std::make_shared(kernel, 1, name); + auto service_thread = std::make_shared(kernel, name); service_threads_manager.QueueWork( [this, service_thread]() { service_threads.emplace(service_thread); }); @@ -774,6 +784,7 @@ struct KernelCore::Impl { /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. std::unordered_map service_interface_factory; + std::unordered_map service_interface_handlers; NamedPortTable named_ports; std::unordered_set server_objects; std::unordered_set registered_objects; @@ -981,10 +992,19 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory& impl->service_interface_factory.emplace(std::move(name), factory); } +void KernelCore::RegisterInterfaceForNamedService(std::string name, + ServiceInterfaceHandlerFn&& handler) { + impl->service_interface_handlers.emplace(std::move(name), handler); +} + KClientPort* KernelCore::CreateNamedServicePort(std::string name) { return impl->CreateNamedServicePort(std::move(name)); } +void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { + impl->RegisterNamedServiceHandler(std::move(name), server_port); +} + void KernelCore::RegisterServerObject(KAutoObject* server_object) { impl->RegisterServerObject(server_object); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 266be2bc4..670f93ee3 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -45,6 +45,7 @@ class KPort; class KProcess; class KResourceLimit; class KScheduler; +class KServerPort; class KServerSession; class KSession; class KSessionRequest; @@ -63,6 +64,8 @@ class TimeManager; using ServiceInterfaceFactory = std::function; +using ServiceInterfaceHandlerFn = std::function; + namespace Init { struct KSlabResourceCounts; } @@ -192,9 +195,15 @@ public: /// Registers a named HLE service, passing a factory used to open a port to that service. void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory); + /// Registers a setup function for the named HLE service. + void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler); + /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); + /// Accepts a session on a port created by CreateNamedServicePort. + void RegisterNamedServiceHandler(std::string name, KServerPort* server_port); + /// Registers a server session or port with the gobal emulation state, to be freed on shutdown. /// This is necessary because we do not emulate processes for HLE sessions and ports. void RegisterServerObject(KAutoObject* server_object); diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index d23d76706..1fc2edf52 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -1,15 +1,17 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include #include #include -#include #include "common/scope_exit.h" #include "common/thread.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -19,101 +21,201 @@ namespace Kernel { class ServiceThread::Impl final { public: - explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit Impl(KernelCore& kernel, const std::string& service_name); ~Impl(); - void QueueSyncRequest(KSession& session, std::shared_ptr&& context); + void WaitAndProcessImpl(); + void SessionClosed(KServerSession* server_session, + std::shared_ptr manager); + void LoopProcess(); + + void RegisterServerSession(KServerSession* session, + std::shared_ptr manager); private: - std::vector threads; - std::queue> requests; - std::mutex queue_mutex; - std::condition_variable_any condition; - const std::string service_name; + KernelCore& kernel; + + std::jthread m_thread; + std::mutex m_session_mutex; + std::vector m_sessions; + std::vector> m_managers; + KEvent* m_wakeup_event; + KProcess* m_process; + std::atomic m_shutdown_requested; + const std::string m_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{service_name}.c_str()); +void ServiceThread::Impl::WaitAndProcessImpl() { + // Create local list of waitable sessions. + std::vector objs; + std::vector> managers; - // 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(); }); - } + { + // Lock to get the list. + std::scoped_lock lk{m_session_mutex}; - if (stop_token.stop_requested()) { - return; - } + // Resize to the needed quantity. + objs.resize(m_sessions.size() + 1); + managers.resize(m_managers.size()); - // Allocate a dummy guest thread for this host thread. - kernel.RegisterHostThread(); + // Copy to our local list. + std::copy(m_sessions.begin(), m_sessions.end(), objs.begin()); + std::copy(m_managers.begin(), m_managers.end(), managers.begin()); - while (true) { - std::function task; + // Insert the wakeup event at the end. + objs.back() = &m_wakeup_event->GetReadableEvent(); + } - { - std::unique_lock lock{queue_mutex}; - condition.wait(lock, stop_token, [this] { return !requests.empty(); }); + // Wait on the list of sessions. + s32 index{-1}; + Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(), + static_cast(objs.size()), -1); + ASSERT(!rc.IsFailure()); + + // If this was the wakeup event, clear it and finish. + if (index >= static_cast(objs.size() - 1)) { + m_wakeup_event->Clear(); + return; + } - if (stop_token.stop_requested()) { - return; - } + // This event is from a server session. + auto* server_session = static_cast(objs[index]); + auto& manager = managers[index]; - if (requests.empty()) { - continue; - } + // Fetch the HLE request context. + std::shared_ptr context; + rc = server_session->ReceiveRequest(&context, manager); - task = std::move(requests.front()); - requests.pop(); - } + // If the session was closed, handle that. + if (rc == ResultSessionClosed) { + SessionClosed(server_session, manager); - task(); - } - }); + // Finish. + return; } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + + // Perform the request. + Result service_rc = manager->CompleteSyncRequest(server_session, *context); + + // Reply to the client. + rc = server_session->SendReply(true); + + if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { + SessionClosed(server_session, manager); + return; + } + + // TODO: handle other cases + ASSERT(rc == ResultSuccess); + ASSERT(service_rc == ResultSuccess); } -void ServiceThread::Impl::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { +void ServiceThread::Impl::SessionClosed(KServerSession* server_session, + std::shared_ptr manager) { { - std::unique_lock lock{queue_mutex}; + // Lock to get the list. + std::scoped_lock lk{m_session_mutex}; + + // Get the index of the session. + const auto index = + std::find(m_sessions.begin(), m_sessions.end(), server_session) - m_sessions.begin(); + ASSERT(index < static_cast(m_sessions.size())); + + // Remove the session and its manager. + m_sessions.erase(m_sessions.begin() + index); + m_managers.erase(m_managers.begin() + index); + } - auto* server_session{&session.GetServerSession()}; + // Close our reference to the server session. + server_session->Close(); +} - // Open a reference to the session to ensure it is not closes while the service request - // completes asynchronously. - server_session->Open(); +void ServiceThread::Impl::LoopProcess() { + Common::SetCurrentThreadName(m_service_name.c_str()); - requests.emplace([server_session, context{std::move(context)}]() { - // Close the reference. - SCOPE_EXIT({ server_session->Close(); }); + kernel.RegisterHostThread(); - // Complete the service request. - server_session->CompleteSyncRequest(*context); - }); + while (!m_shutdown_requested.load()) { + WaitAndProcessImpl(); } - condition.notify_one(); +} + +void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, + std::shared_ptr manager) { + // Open the server session. + server_session->Open(); + + { + // Lock to get the list. + std::scoped_lock lk{m_session_mutex}; + + // Insert the session and manager. + m_sessions.push_back(server_session); + m_managers.push_back(manager); + } + + // Signal the wakeup event. + m_wakeup_event->Signal(); } ServiceThread::Impl::~Impl() { - condition.notify_all(); - for (auto& thread : threads) { - thread.request_stop(); - thread.join(); + // Shut down the processing thread. + m_shutdown_requested.store(true); + m_wakeup_event->Signal(); + m_thread.join(); + + // Lock mutex. + m_session_mutex.lock(); + + // Close all remaining sessions. + for (size_t i = 0; i < m_sessions.size(); i++) { + m_sessions[i]->Close(); } + + // Close event. + m_wakeup_event->GetReadableEvent().Close(); + m_wakeup_event->Close(); + + // Close process. + m_process->Close(); +} + +ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) + : kernel{kernel_}, m_service_name{service_name} { + // Initialize process. + m_process = KProcess::Create(kernel); + KProcess::Initialize(m_process, kernel.System(), service_name, + KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); + + // Reserve a new event from the process resource limit + KScopedResourceReservation event_reservation(m_process, LimitableResource::Events); + ASSERT(event_reservation.Succeeded()); + + // Initialize event. + m_wakeup_event = KEvent::Create(kernel); + m_wakeup_event->Initialize(m_process); + + // Commit the event reservation. + event_reservation.Commit(); + + // Register the event. + KEvent::Register(kernel, m_wakeup_event); + + // Start thread. + m_thread = std::jthread([this] { LoopProcess(); }); } -ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : impl{std::make_unique(kernel, num_threads, name)} {} +ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) + : impl{std::make_unique(kernel, name)} {} ServiceThread::~ServiceThread() = default; -void ServiceThread::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { - impl->QueueSyncRequest(session, std::move(context)); +void ServiceThread::RegisterServerSession(KServerSession* session, + std::shared_ptr manager) { + impl->RegisterServerSession(session, manager); } } // namespace Kernel diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h index c5896f2bd..fb4325531 100644 --- a/src/core/hle/kernel/service_thread.h +++ b/src/core/hle/kernel/service_thread.h @@ -11,13 +11,15 @@ namespace Kernel { class HLERequestContext; class KernelCore; class KSession; +class SessionRequestManager; class ServiceThread final { public: - explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name); + explicit ServiceThread(KernelCore& kernel, const std::string& name); ~ServiceThread(); - void QueueSyncRequest(KSession& session, std::shared_ptr&& context); + void RegisterServerSession(KServerSession* session, + std::shared_ptr manager); private: class Impl; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4aca5b27d..8d2c7d6b7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -24,6 +24,7 @@ #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_table.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_resource_limit.h" @@ -382,10 +383,11 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; - R_TRY(port->CreateSession(std::addressof(session), - std::make_shared(kernel))); + R_TRY(port->CreateSession(std::addressof(session))); port->Close(); + kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); + // Register the session in the table, close the extra reference. handle_table.Register(*out, session); session->Close(); -- cgit v1.2.3