From a9369726147c7499e0016e183d5d56a7b44efe4b Mon Sep 17 00:00:00 2001 From: Liam Date: Sat, 18 Feb 2023 16:26:48 -0500 Subject: service: refactor server architecture Converts services to have their own processes --- src/core/hle/service/sm/sm.cpp | 32 ++++++++++++++++--------------- src/core/hle/service/sm/sm.h | 8 +++++--- src/core/hle/service/sm/sm_controller.cpp | 7 ++++--- 3 files changed, 26 insertions(+), 21 deletions(-) (limited to 'src/core/hle/service/sm') diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 84720094f..0de32b05d 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -12,6 +12,7 @@ #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_server_port.h" #include "core/hle/result.h" +#include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm_controller.h" @@ -22,7 +23,9 @@ constexpr Result ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); 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(Kernel::KernelCore& kernel_) : kernel{kernel_} { + controller_interface = std::make_unique(kernel.System()); +} ServiceManager::~ServiceManager() { for (auto& [name, port] : service_ports) { @@ -43,21 +46,12 @@ static Result ValidateServiceName(const std::string& name) { return ResultSuccess; } -Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) { - self.sm_interface = std::make_shared(self, system); - self.controller_interface = std::make_unique(system); - 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) { CASCADE_CODE(ValidateServiceName(name)); + std::scoped_lock lk{lock}; if (registered_services.find(name) != registered_services.end()) { LOG_ERROR(Service_SM, "Service is already registered! service={}", name); return ERR_ALREADY_REGISTERED; @@ -75,6 +69,7 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions, Result ServiceManager::UnregisterService(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); + std::scoped_lock lk{lock}; const auto iter = registered_services.find(name); if (iter == registered_services.end()) { LOG_ERROR(Service_SM, "Server is not registered! service={}", name); @@ -89,6 +84,8 @@ Result ServiceManager::UnregisterService(const std::string& name) { ResultVal ServiceManager::GetServicePort(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); + + std::scoped_lock lk{lock}; auto it = service_ports.find(name); if (it == service_ports.end()) { LOG_ERROR(Service_SM, "Server is not registered! service={}", name); @@ -154,8 +151,7 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); - auto service = service_manager.GetService(name); - if (port_result.Failed() || !service) { + if (port_result.Failed()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } @@ -167,7 +163,6 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& 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()); @@ -212,7 +207,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) { } SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4}, + : ServiceFramework{system_, "sm:", 4}, service_manager{service_manager_}, kernel{system_.Kernel()} { RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, @@ -232,4 +227,11 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) SM::~SM() = default; +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique(system); + + server_manager->ManageNamedPort("sm:", std::make_shared(system.ServiceManager(), system)); + ServerManager::RunServer(std::move(server_manager)); +} + } // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 02a5dde9e..22ca720f8 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -50,9 +51,6 @@ 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(); @@ -78,6 +76,7 @@ private: std::unique_ptr controller_interface; /// Map of registered services, retrieved using GetServicePort. + std::mutex lock; std::unordered_map registered_services; std::unordered_map service_ports; @@ -85,4 +84,7 @@ private: Kernel::KernelCore& kernel; }; +/// Runs SM services. +void LoopProcess(Core::System& system); + } // namespace Service::SM diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 1cf9dd1c4..f52522d1d 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -10,6 +10,7 @@ #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" +#include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm_controller.h" namespace Service::SM { @@ -48,9 +49,9 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // Commit the session reservation. session_reservation.Commit(); - // Register with manager. - session_manager->SessionHandler().RegisterSession(&session->GetServerSession(), - session_manager); + // Register with server manager. + session_manager->GetServerManager().RegisterSession(&session->GetServerSession(), + session_manager); // We succeeded. IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; -- cgit v1.2.3 From 6e0a33089b97747ea0e3dc9d57e19223d420c98a Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 19 Feb 2023 08:44:54 -0500 Subject: sm:: support service registration deferral --- src/core/hle/kernel/hle_ipc.h | 9 +++ src/core/hle/service/server_manager.cpp | 100 ++++++++++++++++++++++++++++++-- src/core/hle/service/server_manager.h | 16 +++++ src/core/hle/service/sm/sm.cpp | 29 ++++++++- src/core/hle/service/sm/sm.h | 5 ++ 5 files changed, 151 insertions(+), 8 deletions(-) (limited to 'src/core/hle/service/sm') diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 059b21991..6cbc974fe 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -358,6 +358,14 @@ public: return manager.lock(); } + bool GetIsDeferred() const { + return is_deferred; + } + + void SetIsDeferred(bool is_deferred_ = true) { + is_deferred = is_deferred_; + } + private: friend class IPC::ResponseBuilder; @@ -392,6 +400,7 @@ private: u32 domain_offset{}; std::weak_ptr manager{}; + bool is_deferred{false}; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp index 55788ddeb..1b3db3caf 100644 --- a/src/core/hle/service/server_manager.cpp +++ b/src/core/hle/service/server_manager.cpp @@ -25,6 +25,7 @@ constexpr size_t MaximumWaitObjects = 0x40; enum HandleType { Port, Session, + DeferEvent, Event, }; @@ -53,9 +54,18 @@ ServerManager::~ServerManager() { session->Close(); } + for (const auto& request : m_deferrals) { + request.session->Close(); + } + // Close event. m_event->GetReadableEvent().Close(); m_event->Close(); + + if (m_deferral_event) { + m_deferral_event->GetReadableEvent().Close(); + // Write event is owned by ServiceManager + } } void ServerManager::RunServer(std::unique_ptr&& server_manager) { @@ -142,6 +152,21 @@ Result ServerManager::ManageNamedPort(const std::string& service_name, R_SUCCEED(); } +Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) { + // Create a new event. + m_deferral_event = Kernel::KEvent::Create(m_system.Kernel()); + ASSERT(m_deferral_event != nullptr); + + // Initialize the event. + m_deferral_event->Initialize(nullptr); + + // Set the output. + *out_event = m_deferral_event; + + // We succeeded. + R_SUCCEED(); +} + void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) { for (size_t i = 0; i < num_threads; i++) { auto thread_name = fmt::format("{}:{}", name, i + 1); @@ -207,6 +232,11 @@ Result ServerManager::WaitAndProcessImpl() { } } + // Add the deferral wakeup event. + if (m_deferral_event != nullptr) { + AddWaiter(std::addressof(m_deferral_event->GetReadableEvent()), HandleType::DeferEvent); + } + // Add the wakeup event. AddWaiter(std::addressof(m_event->GetReadableEvent()), HandleType::Event); @@ -270,6 +300,23 @@ Result ServerManager::WaitAndProcessImpl() { // Finish. R_RETURN(this->OnSessionEvent(session, std::move(manager))); } + case HandleType::DeferEvent: { + // Clear event. + ASSERT(R_SUCCEEDED(m_deferral_event->Clear())); + + // Drain the list of deferrals while we process. + std::list deferrals; + { + std::scoped_lock ll{m_list_mutex}; + m_deferrals.swap(deferrals); + } + + // Allow other threads to serve. + sl.unlock(); + + // Finish. + R_RETURN(this->OnDeferralEvent(std::move(deferrals))); + } case HandleType::Event: { // Clear event and finish. R_RETURN(m_event->Clear()); @@ -308,7 +355,6 @@ Result ServerManager::OnPortEvent(Kernel::KServerPort* port, Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, std::shared_ptr&& manager) { Result rc{ResultSuccess}; - Result service_rc{ResultSuccess}; // Try to receive a message. std::shared_ptr context; @@ -324,16 +370,43 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, } ASSERT(R_SUCCEEDED(rc)); + RequestState request{ + .session = session, + .context = std::move(context), + .manager = std::move(manager), + }; + + // Complete the sync request with deferral handling. + R_RETURN(this->CompleteSyncRequest(std::move(request))); +} + +Result ServerManager::CompleteSyncRequest(RequestState&& request) { + Result rc{ResultSuccess}; + Result service_rc{ResultSuccess}; + + // Mark the request as not deferred. + request.context->SetIsDeferred(false); + // Complete the request. We have exclusive access to this session. - service_rc = manager->CompleteSyncRequest(session, *context); + service_rc = request.manager->CompleteSyncRequest(request.session, *request.context); + + // If we've been deferred, we're done. + if (request.context->GetIsDeferred()) { + // Insert into deferral list. + std::scoped_lock ll{m_list_mutex}; + m_deferrals.emplace_back(std::move(request)); + + // Finish. + R_SUCCEED(); + } // Send the reply. - rc = session->SendReplyHLE(); + rc = request.session->SendReplyHLE(); // If the session has been closed, we're done. if (rc == Kernel::ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) { // Close the session. - session->Close(); + request.session->Close(); // Finish. R_SUCCEED(); @@ -345,7 +418,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, // Reinsert the session. { std::scoped_lock ll{m_list_mutex}; - m_sessions.emplace(session, std::move(manager)); + m_sessions.emplace(request.session, std::move(request.manager)); } // Signal the wakeup event. @@ -355,4 +428,21 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session, R_SUCCEED(); } +Result ServerManager::OnDeferralEvent(std::list&& deferrals) { + ON_RESULT_FAILURE { + std::scoped_lock ll{m_list_mutex}; + m_deferrals.splice(m_deferrals.end(), deferrals); + }; + + while (!deferrals.empty()) { + RequestState request = deferrals.front(); + deferrals.pop_front(); + + // Try again to complete the request. + R_TRY(this->CompleteSyncRequest(std::move(request))); + } + + R_SUCCEED(); +} + } // namespace Service diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h index b26557172..57b954ae8 100644 --- a/src/core/hle/service/server_manager.h +++ b/src/core/hle/service/server_manager.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,7 @@ class System; } namespace Kernel { +class HLERequestContext; class KEvent; class KServerPort; class KServerSession; @@ -42,6 +44,7 @@ public: Result ManageNamedPort(const std::string& service_name, std::shared_ptr&& handler, u32 max_sessions = 64); + Result ManageDeferral(Kernel::KEvent** out_event); Result LoopProcess(); void StartAdditionalHostThreads(const char* name, size_t num_threads); @@ -49,12 +52,16 @@ public: static void RunServer(std::unique_ptr&& server); private: + struct RequestState; + Result LoopProcessImpl(); Result WaitAndProcessImpl(); Result OnPortEvent(Kernel::KServerPort* port, std::shared_ptr&& handler); Result OnSessionEvent(Kernel::KServerSession* session, std::shared_ptr&& manager); + Result OnDeferralEvent(std::list&& deferrals); + Result CompleteSyncRequest(RequestState&& state); private: Core::System& m_system; @@ -65,6 +72,15 @@ private: std::map> m_ports{}; std::map> m_sessions{}; Kernel::KEvent* m_event{}; + Kernel::KEvent* m_deferral_event{}; + + // Deferral tracking + struct RequestState { + Kernel::KServerSession* session; + std::shared_ptr context; + std::shared_ptr manager; + }; + std::list m_deferrals{}; // Host state tracking std::atomic m_stopped{}; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 0de32b05d..6eba48f03 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -32,6 +32,10 @@ ServiceManager::~ServiceManager() { port->GetClientPort().Close(); port->GetServerPort().Close(); } + + if (deferral_event) { + deferral_event->Close(); + } } void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { @@ -62,6 +66,9 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions, service_ports.emplace(name, port); registered_services.emplace(name, handler); + if (deferral_event) { + deferral_event->Signal(); + } return ResultSuccess; } @@ -88,7 +95,7 @@ ResultVal ServiceManager::GetServicePort(const std::string& name std::scoped_lock lk{lock}; auto it = service_ports.find(name); if (it == service_ports.end()) { - LOG_ERROR(Service_SM, "Server is not registered! service={}", name); + LOG_WARNING(Service_SM, "Server is not registered! service={}", name); return ERR_SERVICE_NOT_REGISTERED; } @@ -113,6 +120,11 @@ void SM::Initialize(Kernel::HLERequestContext& ctx) { void SM::GetService(Kernel::HLERequestContext& ctx) { auto result = GetServiceImpl(ctx); + if (ctx.GetIsDeferred()) { + // Don't overwrite the command buffer. + return; + } + if (result.Succeeded()) { IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(result.Code()); @@ -125,6 +137,11 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { void SM::GetServiceTipc(Kernel::HLERequestContext& ctx) { auto result = GetServiceImpl(ctx); + if (ctx.GetIsDeferred()) { + // Don't overwrite the command buffer. + return; + } + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(result.Code()); rb.PushMoveObjects(result.Succeeded() ? result.Unwrap() : nullptr); @@ -152,8 +169,9 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); if (port_result.Failed()) { - LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); - return port_result.Code(); + LOG_INFO(Service_SM, "Waiting for service {} to become available", name); + ctx.SetIsDeferred(); + return ERR_SERVICE_NOT_REGISTERED; } auto& port = port_result.Unwrap(); @@ -228,8 +246,13 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) SM::~SM() = default; void LoopProcess(Core::System& system) { + auto& service_manager = system.ServiceManager(); auto server_manager = std::make_unique(system); + Kernel::KEvent* deferral_event{}; + server_manager->ManageDeferral(&deferral_event); + service_manager.SetDeferralEvent(deferral_event); + server_manager->ManageNamedPort("sm:", std::make_shared(system.ServiceManager(), system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 22ca720f8..b7eeafdd6 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -71,6 +71,10 @@ public: void InvokeControlRequest(Kernel::HLERequestContext& context); + void SetDeferralEvent(Kernel::KEvent* deferral_event_) { + deferral_event = deferral_event_; + } + private: std::shared_ptr sm_interface; std::unique_ptr controller_interface; @@ -82,6 +86,7 @@ private: /// Kernel context Kernel::KernelCore& kernel; + Kernel::KEvent* deferral_event{}; }; /// Runs SM services. -- cgit v1.2.3 From 72e5552409305fe57781b83c3145fb2b66552be2 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 21 Feb 2023 12:19:12 -0500 Subject: sm:: fix lingering session initialization issues --- src/core/hle/kernel/hle_ipc.h | 12 ++++++++++++ src/core/hle/service/sm/sm.cpp | 9 +++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src/core/hle/service/sm') diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 6cbc974fe..b4364f984 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -147,9 +147,21 @@ public: return server_manager; } + // TODO: remove this when sm: is implemented with the proper IUserInterface + // abstraction, creating a new C++ handler object for each session: + + bool GetIsInitializedForSm() const { + return is_initialized_for_sm; + } + + void SetIsInitializedForSm() { + is_initialized_for_sm = true; + } + private: bool convert_to_domain{}; bool is_domain{}; + bool is_initialized_for_sm{}; SessionRequestHandlerPtr session_handler; std::vector domain_handlers; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 6eba48f03..53c877836 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -112,7 +112,7 @@ ResultVal ServiceManager::GetServicePort(const std::string& name void SM::Initialize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SM, "called"); - is_initialized = true; + ctx.GetManager()->SetIsInitializedForSm(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -159,7 +159,7 @@ static std::string PopServiceName(IPC::RequestParser& rp) { } ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& ctx) { - if (!is_initialized) { + if (!ctx.GetManager()->GetIsInitializedForSm()) { return ERR_NOT_INITIALIZED; } @@ -168,6 +168,11 @@ ResultVal SM::GetServiceImpl(Kernel::HLERequestContext& // Find the named port. auto port_result = service_manager.GetServicePort(name); + if (port_result.Code() == ERR_INVALID_NAME) { + LOG_ERROR(Service_SM, "Invalid service name '{}'", name); + return ERR_INVALID_NAME; + } + if (port_result.Failed()) { LOG_INFO(Service_SM, "Waiting for service {} to become available", name); ctx.SetIsDeferred(); -- cgit v1.2.3