From 983f2b70741f17f30fe2321451f10cabecc013d2 Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 16 Oct 2022 01:53:56 -0400 Subject: [PATCH 1/6] kernel: invert session request handling flow --- src/core/hle/ipc_helpers.h | 17 +- 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 | 238 +++++++++++++++------- src/core/hle/kernel/service_thread.h | 6 +- src/core/hle/kernel/svc.cpp | 6 +- src/core/hle/service/service.cpp | 13 +- src/core/hle/service/service.h | 4 + src/core/hle/service/sm/sm.cpp | 13 +- src/core/hle/service/sm/sm.h | 1 + src/core/hle/service/sm/sm_controller.cpp | 21 +- 22 files changed, 424 insertions(+), 282 deletions(-) diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 18fde8bd6..3bb111748 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -86,13 +86,13 @@ public: u32 num_domain_objects{}; const bool always_move_handles{ (static_cast(flags) & static_cast(Flags::AlwaysMoveHandles)) != 0}; - if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) { + if (!ctx.GetManager()->IsDomain() || always_move_handles) { num_handles_to_move = num_objects_to_move; } else { num_domain_objects = num_objects_to_move; } - if (ctx.Session()->GetSessionRequestManager()->IsDomain()) { + if (ctx.GetManager()->IsDomain()) { raw_data_size += static_cast(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); ctx.write_size += num_domain_objects; @@ -125,8 +125,7 @@ public: if (!ctx.IsTipc()) { AlignWithPadding(); - if (ctx.Session()->GetSessionRequestManager()->IsDomain() && - ctx.HasDomainMessageHeader()) { + if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); @@ -146,18 +145,18 @@ public: template void PushIpcInterface(std::shared_ptr iface) { - if (context->Session()->GetSessionRequestManager()->IsDomain()) { + if (context->GetManager()->IsDomain()) { context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( Kernel::LimitableResource::Sessions, 1); auto* session = Kernel::KSession::Create(kernel); - session->Initialize(nullptr, iface->GetServiceName(), - std::make_shared(kernel)); + session->Initialize(nullptr, iface->GetServiceName()); + iface->RegisterSession(&session->GetServerSession(), + std::make_shared(kernel)); context->AddMoveObject(&session->GetClientSession()); - iface->ClientConnected(&session->GetServerSession()); } } @@ -387,7 +386,7 @@ public: template std::weak_ptr PopIpcInterface() { - ASSERT(context->Session()->GetSessionRequestManager()->IsDomain()); + ASSERT(context->GetManager()->IsDomain()); ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainHandler(Pop() - 1); } 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 (stop_token.stop_requested()) { - return; - } + // If this was the wakeup event, clear it and finish. + if (index >= static_cast(objs.size() - 1)) { + m_wakeup_event->Clear(); + return; + } - if (requests.empty()) { - continue; - } + // This event is from a server session. + auto* server_session = static_cast(objs[index]); + auto& manager = managers[index]; - task = std::move(requests.front()); - requests.pop(); - } + // Fetch the HLE request context. + std::shared_ptr context; + rc = server_session->ReceiveRequest(&context, manager); - task(); - } - }); + // If the session was closed, handle that. + if (rc == ResultSessionClosed) { + SessionClosed(server_session, manager); + + // 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::SessionClosed(KServerSession* server_session, + std::shared_ptr manager) { + { + // 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); + } + + // Close our reference to the server session. + server_session->Close(); +} + +void ServiceThread::Impl::LoopProcess() { + Common::SetCurrentThreadName(m_service_name.c_str()); + + kernel.RegisterHostThread(); + + while (!m_shutdown_requested.load()) { + WaitAndProcessImpl(); } } -void ServiceThread::Impl::QueueSyncRequest(KSession& session, - std::shared_ptr&& context) { +void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, + std::shared_ptr manager) { + // Open the server session. + server_session->Open(); + { - std::unique_lock lock{queue_mutex}; + // Lock to get the list. + std::scoped_lock lk{m_session_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); - }); + // Insert the session and manager. + m_sessions.push_back(server_session); + m_managers.push_back(manager); } - condition.notify_one(); + + // 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::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) - : impl{std::make_unique(kernel, num_threads, name)} {} +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, 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(); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5db6588e4..0913a8065 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -99,6 +99,10 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying const auto guard = LockService(); + + if (named_port != nullptr) { + named_port->Close(); + } } void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { @@ -115,13 +119,12 @@ Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { ASSERT(!service_registered); - auto* port = Kernel::KPort::Create(kernel); - port->Initialize(max_sessions, false, service_name); - port->GetServerPort().SetSessionHandler(shared_from_this()); + named_port = Kernel::KPort::Create(kernel); + named_port->Initialize(max_sessions, false, service_name); service_registered = true; - return port->GetClientPort(); + return named_port->GetClientPort(); } void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { @@ -199,7 +202,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, switch (ctx.GetCommandType()) { case IPC::CommandType::Close: case IPC::CommandType::TIPC_Close: { - session.Close(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); result = IPC::ERR_REMOTE_PROCESS_DEAD; @@ -244,6 +246,7 @@ Services::Services(std::shared_ptr& sm, Core::System& system system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory); + system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler); Account::InstallInterfaces(system); AM::InstallInterfaces(*sm, *nv_flinger, system); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index ec9deeee4..22e2119d7 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,6 +20,7 @@ class System; namespace Kernel { class HLERequestContext; class KClientPort; +class KPort; class KServerSession; class ServiceThread; } // namespace Kernel @@ -98,6 +99,9 @@ protected: /// Identifier string used to connect to the service. std::string service_name; + /// Port used by ManageNamedPort. + Kernel::KPort* named_port{}; + private: template friend class ServiceFramework; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index cb6c0e96f..c1f535d71 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -43,6 +43,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core return self.sm_interface->CreatePort(); } +void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) { + self.sm_interface->AcceptSession(server_port); +} + Result ServiceManager::RegisterService(std::string name, u32 max_sessions, Kernel::SessionRequestHandlerPtr handler) { @@ -83,7 +87,6 @@ ResultVal ServiceManager::GetServicePort(const std::string& name port->Initialize(ServerSessionCountMax, false, name); auto handler = it->second; - port->GetServerPort().SetSessionHandler(std::move(handler)); return port; } @@ -144,7 +147,8 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); - if (port_result.Failed()) { + auto service = service_manager.GetService(name); + if (port_result.Failed() || !service) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } @@ -156,12 +160,11 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port->GetClientPort().CreateSession( - std::addressof(session), std::make_shared(kernel)); - result.IsError()) { + if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } + service->AcceptSession(&port->GetServerPort()); LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId()); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 878decc6f..cfe370652 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -51,6 +51,7 @@ private: class ServiceManager { public: static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system); + static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port); explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 46a8439d8..940c33478 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -15,10 +15,9 @@ namespace Service::SM { void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { - ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(), - "Session is already a domain"); + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain"); LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); - ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd(); + ctx.GetManager()->ConvertToDomainOnRequestEnd(); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -29,30 +28,32 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service, "called"); auto& process = *ctx.GetThread().GetOwnerProcess(); - auto& parent_session = *ctx.Session()->GetParent(); - auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); - auto& session_handler = session_manager->SessionHandler(); + auto session_manager = ctx.GetManager(); // FIXME: this is duplicated from the SVC, it should just call it instead // once this is a proper process + // Declare the session we're going to allocate. + Kernel::KSession* session; + // Reserve a new session from the process resource limit. Kernel::KScopedResourceReservation session_reservation(&process, Kernel::LimitableResource::Sessions); ASSERT(session_reservation.Succeeded()); // Create the session. - Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); + session = Kernel::KSession::Create(system.Kernel()); ASSERT(session != nullptr); // Initialize the session. - session->Initialize(nullptr, parent_session.GetName(), session_manager); + session->Initialize(nullptr, ""); // Commit the session reservation. session_reservation.Commit(); - // Register the session. - session_handler.ClientConnected(&session->GetServerSession()); + // Register with manager. + session_manager->SessionHandler().RegisterSession(&session->GetServerSession(), + session_manager); // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; From 7837185f0ab54f76aa57c65676b10179465fe42d Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 24 Oct 2022 21:23:53 -0400 Subject: [PATCH 2/6] service_thread: convert to map for session management --- src/core/hle/kernel/service_thread.cpp | 44 ++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 1fc2edf52..1d8775504 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -37,8 +38,7 @@ private: std::jthread m_thread; std::mutex m_session_mutex; - std::vector m_sessions; - std::vector> m_managers; + std::map> m_sessions; KEvent* m_wakeup_event; KProcess* m_process; std::atomic m_shutdown_requested; @@ -51,19 +51,21 @@ void ServiceThread::Impl::WaitAndProcessImpl() { std::vector> managers; { - // Lock to get the list. + // Lock to get the set. std::scoped_lock lk{m_session_mutex}; - // Resize to the needed quantity. - objs.resize(m_sessions.size() + 1); - managers.resize(m_managers.size()); + // Reserve the needed quantity. + objs.reserve(m_sessions.size() + 1); + managers.reserve(m_sessions.size()); // 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()); + for (const auto& [session, manager] : m_sessions) { + objs.push_back(session); + managers.push_back(manager); + } // Insert the wakeup event at the end. - objs.back() = &m_wakeup_event->GetReadableEvent(); + objs.push_back(&m_wakeup_event->GetReadableEvent()); } // Wait on the list of sessions. @@ -116,17 +118,11 @@ void ServiceThread::Impl::WaitAndProcessImpl() { void ServiceThread::Impl::SessionClosed(KServerSession* server_session, std::shared_ptr manager) { { - // Lock to get the list. + // Lock to get the set. 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); + // Erase the session. + ASSERT(m_sessions.erase(server_session) == 1); } // Close our reference to the server session. @@ -149,12 +145,11 @@ void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session, server_session->Open(); { - // Lock to get the list. + // Lock to get the set. std::scoped_lock lk{m_session_mutex}; // Insert the session and manager. - m_sessions.push_back(server_session); - m_managers.push_back(manager); + m_sessions[server_session] = manager; } // Signal the wakeup event. @@ -171,10 +166,13 @@ ServiceThread::Impl::~Impl() { m_session_mutex.lock(); // Close all remaining sessions. - for (size_t i = 0; i < m_sessions.size(); i++) { - m_sessions[i]->Close(); + for (const auto& [server_session, manager] : m_sessions) { + server_session->Close(); } + // Destroy remaining managers. + m_sessions.clear(); + // Close event. m_wakeup_event->GetReadableEvent().Close(); m_wakeup_event->Close(); From 7aa91c8d9ceb21e631e52c7ac30e47af2ec5a089 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 26 Oct 2022 17:32:14 -0400 Subject: [PATCH 3/6] k_server_session: add SendReplyHLE --- src/core/hle/kernel/k_server_session.h | 4 ++++ src/core/hle/kernel/service_thread.cpp | 2 +- src/core/hle/service/sm/sm_controller.cpp | 5 +---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index e4698d3f5..6e189af8b 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -55,6 +55,10 @@ public: Result ReceiveRequest(std::shared_ptr* out_context = nullptr, std::weak_ptr manager = {}); + Result SendReplyHLE() { + return SendReply(true); + } + private: /// Frees up waiting client sessions when this server session is about to die void CleanupRequests(); diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 1d8775504..c8fe42537 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -103,7 +103,7 @@ void ServiceThread::Impl::WaitAndProcessImpl() { Result service_rc = manager->CompleteSyncRequest(server_session, *context); // Reply to the client. - rc = server_session->SendReply(true); + rc = server_session->SendReplyHLE(); if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { SessionClosed(server_session, manager); diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 940c33478..69e0fe808 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -33,16 +33,13 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // FIXME: this is duplicated from the SVC, it should just call it instead // once this is a proper process - // Declare the session we're going to allocate. - Kernel::KSession* session; - // Reserve a new session from the process resource limit. Kernel::KScopedResourceReservation session_reservation(&process, Kernel::LimitableResource::Sessions); ASSERT(session_reservation.Succeeded()); // Create the session. - session = Kernel::KSession::Create(system.Kernel()); + Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); ASSERT(session != nullptr); // Initialize the session. From 222838332209db34cd70687caaa6997c31de085b Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 19:20:55 -0400 Subject: [PATCH 4/6] kernel: fix port tracking --- src/core/hle/kernel/k_server_port.cpp | 5 ---- src/core/hle/kernel/kernel.cpp | 35 +-------------------------- src/core/hle/kernel/kernel.h | 8 ------ src/core/hle/kernel/svc.cpp | 1 - src/core/hle/service/service.cpp | 4 ++- 5 files changed, 4 insertions(+), 49 deletions(-) diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 12e0c3ffb..16968ba97 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -19,8 +19,6 @@ 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 { @@ -63,9 +61,6 @@ void KServerPort::Destroy() { // Close our reference to our parent. parent->Close(); - - // Ensure that the global list tracking server objects does not hold on to a reference. - kernel.UnregisterServerObject(this); } bool KServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 29e122dfd..054898638 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -185,17 +185,6 @@ struct KernelCore::Impl { } void CloseServices() { - // Close all open server sessions and ports. - std::unordered_set server_objects_; - { - std::scoped_lock lk(server_objects_lock); - server_objects_ = server_objects; - server_objects.clear(); - } - for (auto* server_object : server_objects_) { - server_object->Close(); - } - // Ensures all service threads gracefully shutdown. ClearServiceThreads(); } @@ -699,9 +688,7 @@ struct KernelCore::Impl { return {}; } - KClientPort* port = &search->second(system.ServiceManager(), system); - RegisterServerObject(&port->GetParent()->GetServerPort()); - return port; + return &search->second(system.ServiceManager(), system); } void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) { @@ -713,16 +700,6 @@ struct KernelCore::Impl { search->second(system.ServiceManager(), server_port); } - void RegisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.insert(server_object); - } - - void UnregisterServerObject(KAutoObject* server_object) { - std::scoped_lock lk(server_objects_lock); - server_objects.erase(server_object); - } - std::weak_ptr CreateServiceThread(KernelCore& kernel, const std::string& name) { auto service_thread = std::make_shared(kernel, name); @@ -755,7 +732,6 @@ struct KernelCore::Impl { service_thread_barrier.Sync(); } - std::mutex server_objects_lock; std::mutex registered_objects_lock; std::mutex registered_in_use_objects_lock; @@ -786,7 +762,6 @@ struct KernelCore::Impl { 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; std::unordered_set registered_in_use_objects; @@ -1005,14 +980,6 @@ void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* serv impl->RegisterNamedServiceHandler(std::move(name), server_port); } -void KernelCore::RegisterServerObject(KAutoObject* server_object) { - impl->RegisterServerObject(server_object); -} - -void KernelCore::UnregisterServerObject(KAutoObject* server_object) { - impl->UnregisterServerObject(server_object); -} - void KernelCore::RegisterKernelObject(KAutoObject* object) { std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.insert(object); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 670f93ee3..4ae6b3923 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -204,14 +204,6 @@ public: /// 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); - - /// Unregisters a server session or port previously registered with RegisterServerSession when - /// it was destroyed during the current emulation session. - void UnregisterServerObject(KAutoObject* server_object); - /// Registers all kernel objects with the global emulation state, this is purely for tracking /// leaks after emulation has been shutdown. void RegisterKernelObject(KAutoObject* object); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8d2c7d6b7..4c819f4b6 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -384,7 +384,6 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n // Create a session. KClientSession* session{}; R_TRY(port->CreateSession(std::addressof(session))); - port->Close(); kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0913a8065..6a64c6005 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -101,7 +101,9 @@ ServiceFrameworkBase::~ServiceFrameworkBase() { const auto guard = LockService(); if (named_port != nullptr) { - named_port->Close(); + named_port->GetClientPort().Close(); + named_port->GetServerPort().Close(); + named_port = nullptr; } } From 633411c20f9759a9a5f1a3bf15466b387dce9a5a Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 20:02:33 -0400 Subject: [PATCH 5/6] kernel: fix single core for service threads --- src/core/hle/kernel/kernel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 054898638..09c36ee09 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -336,6 +336,8 @@ struct KernelCore::Impl { return this_id; } + static inline thread_local bool is_phantom_mode_for_singlecore{false}; + bool IsPhantomModeForSingleCore() const { return is_phantom_mode_for_singlecore; } @@ -800,7 +802,6 @@ struct KernelCore::Impl { bool is_multicore{}; std::atomic_bool is_shutting_down{}; - bool is_phantom_mode_for_singlecore{}; u32 single_core_thread_id{}; std::array svc_ticks{}; From 77b74f5d95626422f59b508cd2b355135ed256ca Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 30 Oct 2022 22:22:14 -0400 Subject: [PATCH 6/6] sm:: avoid excessive port recreation --- src/core/hle/service/service.cpp | 10 ++++++---- src/core/hle/service/sm/sm.cpp | 31 +++++++++++++++++-------------- src/core/hle/service/sm/sm.h | 1 + 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 6a64c6005..5ab41c0c4 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -119,12 +119,14 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); - ASSERT(!service_registered); + if (named_port == nullptr) { + ASSERT(!service_registered); - named_port = Kernel::KPort::Create(kernel); - named_port->Initialize(max_sessions, false, service_name); + named_port = Kernel::KPort::Create(kernel); + named_port->Initialize(max_sessions, false, service_name); - service_registered = true; + service_registered = true; + } return named_port->GetClientPort(); } diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index c1f535d71..84720094f 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6); constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} -ServiceManager::~ServiceManager() = default; + +ServiceManager::~ServiceManager() { + for (auto& [name, port] : service_ports) { + port->GetClientPort().Close(); + port->GetServerPort().Close(); + } +} void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); @@ -57,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions, return ERR_ALREADY_REGISTERED; } - registered_services.emplace(std::move(name), handler); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, false, name); + + service_ports.emplace(name, port); + registered_services.emplace(name, handler); return ResultSuccess; } @@ -72,23 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) { } registered_services.erase(iter); + service_ports.erase(name); + return ResultSuccess; } ResultVal ServiceManager::GetServicePort(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); - auto it = registered_services.find(name); - if (it == registered_services.end()) { + auto it = service_ports.find(name); + if (it == service_ports.end()) { LOG_ERROR(Service_SM, "Server is not registered! service={}", name); return ERR_SERVICE_NOT_REGISTERED; } - auto* port = Kernel::KPort::Create(kernel); - - port->Initialize(ServerSessionCountMax, false, name); - auto handler = it->second; - - return port; + return it->second; } /** @@ -153,10 +160,6 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& return port_result.Code(); } auto& port = port_result.Unwrap(); - SCOPE_EXIT({ - port->GetClientPort().Close(); - port->GetServerPort().Close(); - }); // Create a new session. Kernel::KClientSession* session{}; diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index cfe370652..02a5dde9e 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -79,6 +79,7 @@ private: /// Map of registered services, retrieved using GetServicePort. std::unordered_map registered_services; + std::unordered_map service_ports; /// Kernel context Kernel::KernelCore& kernel;