// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/scope_exit.h" #include "core/core.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_object_name.h" #include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/svc_results.h" #include "core/hle/service/hle_ipc.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm.h" namespace Service { enum class UserDataTag { Port, Session, DeferEvent, }; class Port : public MultiWaitHolder, public Common::IntrusiveListBaseNode { public: explicit Port(Kernel::KServerPort* server_port, SessionRequestHandlerFactory&& handler_factory) : MultiWaitHolder(server_port), m_handler_factory(std::move(handler_factory)) { this->SetUserData(static_cast(UserDataTag::Port)); } ~Port() { this->GetNativeHandle()->Close(); } SessionRequestHandlerPtr CreateHandler() { return m_handler_factory(); } private: const SessionRequestHandlerFactory m_handler_factory; }; class Session : public MultiWaitHolder, public Common::IntrusiveListBaseNode { public: explicit Session(Kernel::KServerSession* server_session, std::shared_ptr&& manager) : MultiWaitHolder(server_session), m_manager(std::move(manager)) { this->SetUserData(static_cast(UserDataTag::Session)); } ~Session() { this->GetNativeHandle()->Close(); } std::shared_ptr& GetManager() { return m_manager; } std::shared_ptr& GetContext() { return m_context; } private: std::shared_ptr m_manager; std::shared_ptr m_context; }; ServerManager::ServerManager(Core::System& system) : m_system{system}, m_selection_mutex{system} { // Initialize event. m_wakeup_event = Kernel::KEvent::Create(system.Kernel()); m_wakeup_event->Initialize(nullptr); // Register event. Kernel::KEvent::Register(system.Kernel(), m_wakeup_event); // Link to holder. m_wakeup_holder.emplace(std::addressof(m_wakeup_event->GetReadableEvent())); m_wakeup_holder->LinkToMultiWait(std::addressof(m_deferred_list)); } ServerManager::~ServerManager() { // Signal stop. m_stop_source.request_stop(); m_wakeup_event->Signal(); // Wait for processing to stop. m_stopped.Wait(); m_threads.clear(); // Clean up ports. for (auto it = m_servers.begin(); it != m_servers.end(); it = m_servers.erase(it)) { delete std::addressof(*it); } // Clean up sessions. for (auto it = m_sessions.begin(); it != m_sessions.end(); it = m_sessions.erase(it)) { delete std::addressof(*it); } // Close wakeup event. m_wakeup_event->GetReadableEvent().Close(); m_wakeup_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) { server_manager->m_system.RunServer(std::move(server_manager)); } Result ServerManager::RegisterSession(Kernel::KServerSession* server_session, std::shared_ptr manager) { // We are taking ownership of the server session, so don't open it. auto* session = new Session(server_session, std::move(manager)); // Begin tracking the server session. { std::scoped_lock ll{m_deferred_list_mutex}; m_sessions.push_back(*session); } // Register to wait on the session. this->LinkToDeferredList(session); R_SUCCEED(); } Result ServerManager::RegisterNamedService(const std::string& service_name, SessionRequestHandlerFactory&& handler_factory, u32 max_sessions) { // Add the new server to sm: and get the moved server port. Kernel::KServerPort* server_port{}; R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name, max_sessions, handler_factory)); // We are taking ownership of the server port, so don't open it. auto* server = new Port(server_port, std::move(handler_factory)); // Begin tracking the server port. { std::scoped_lock ll{m_deferred_list_mutex}; m_servers.push_back(*server); } // Register to wait on the server port. this->LinkToDeferredList(server); R_SUCCEED(); } Result ServerManager::RegisterNamedService(const std::string& service_name, std::shared_ptr&& handler, u32 max_sessions) { // Make the factory. const auto HandlerFactory = [handler]() { return handler; }; // Register the service with the new factory. R_RETURN(this->RegisterNamedService(service_name, std::move(HandlerFactory), max_sessions)); } Result ServerManager::ManageNamedPort(const std::string& service_name, SessionRequestHandlerFactory&& handler_factory, u32 max_sessions) { // Create a new port. auto* port = Kernel::KPort::Create(m_system.Kernel()); port->Initialize(max_sessions, false, 0); // Register the port. Kernel::KPort::Register(m_system.Kernel(), port); // Ensure that our reference to the port is closed if we fail to register it. SCOPE_EXIT({ port->GetClientPort().Close(); port->GetServerPort().Close(); }); // Register the object name with the kernel. R_TRY(Kernel::KObjectName::NewFromName(m_system.Kernel(), std::addressof(port->GetClientPort()), service_name.c_str())); // Open a new reference to the server port. port->GetServerPort().Open(); // Transfer ownership into a new port object. auto* server = new Port(std::addressof(port->GetServerPort()), std::move(handler_factory)); // Begin tracking the port. { std::scoped_lock ll{m_deferred_list_mutex}; m_servers.push_back(*server); } // Register to wait on the port. this->LinkToDeferredList(server); // We succeeded. 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); // Register the event. Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event); // Set the output. *out_event = m_deferral_event; // Register to wait on the event. m_deferral_holder.emplace(std::addressof(m_deferral_event->GetReadableEvent())); m_deferral_holder->SetUserData(static_cast(UserDataTag::DeferEvent)); this->LinkToDeferredList(std::addressof(*m_deferral_holder)); // 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); m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread( std::move(thread_name), [&] { this->LoopProcessImpl(); })); } } Result ServerManager::LoopProcess() { SCOPE_EXIT({ m_stopped.Set(); }); R_RETURN(this->LoopProcessImpl()); } void ServerManager::LinkToDeferredList(MultiWaitHolder* holder) { // Link. { std::scoped_lock lk{m_deferred_list_mutex}; holder->LinkToMultiWait(std::addressof(m_deferred_list)); } // Signal the wakeup event. m_wakeup_event->Signal(); } void ServerManager::LinkDeferred() { std::scoped_lock lk{m_deferred_list_mutex}; m_multi_wait.MoveAll(std::addressof(m_deferred_list)); } MultiWaitHolder* ServerManager::WaitSignaled() { // Ensure we are the only thread waiting for this server. std::scoped_lock lk{m_selection_mutex}; while (true) { this->LinkDeferred(); // If we're done, return before we start waiting. if (m_stop_source.stop_requested()) { return nullptr; } auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); if (selected == std::addressof(*m_wakeup_holder)) { // Clear and restart if we were woken up. m_wakeup_event->Clear(); } else { // Unlink and handle the event. selected->UnlinkFromMultiWait(); return selected; } } } Result ServerManager::Process(MultiWaitHolder* holder) { switch (static_cast(holder->GetUserData())) { case UserDataTag::Session: R_RETURN(this->OnSessionEvent(static_cast(holder))); case UserDataTag::Port: R_RETURN(this->OnPortEvent(static_cast(holder))); case UserDataTag::DeferEvent: R_RETURN(this->OnDeferralEvent()); default: UNREACHABLE(); } } bool ServerManager::WaitAndProcessImpl() { if (auto* signaled_holder = this->WaitSignaled(); signaled_holder != nullptr) { R_ASSERT(this->Process(signaled_holder)); return true; } else { return false; } } Result ServerManager::LoopProcessImpl() { while (!m_stop_source.stop_requested()) { this->WaitAndProcessImpl(); } R_SUCCEED(); } Result ServerManager::OnPortEvent(Port* server) { // Accept a new server session. auto* server_port = static_cast(server->GetNativeHandle()); Kernel::KServerSession* server_session = server_port->AcceptSession(); ASSERT(server_session != nullptr); // Create the session manager and install the handler. auto manager = std::make_shared(m_system.Kernel(), *this); manager->SetSessionHandler(server->CreateHandler()); // Create and register the new session. this->RegisterSession(server_session, std::move(manager)); // Resume tracking the port. this->LinkToDeferredList(server); // We succeeded. R_SUCCEED(); } Result ServerManager::OnSessionEvent(Session* session) { Result res = ResultSuccess; // Try to receive a message. auto* server_session = static_cast(session->GetNativeHandle()); res = server_session->ReceiveRequestHLE(&session->GetContext(), session->GetManager()); // If the session has been closed, we're done. if (res == Kernel::ResultSessionClosed) { this->DestroySession(session); R_SUCCEED(); } R_ASSERT(res); // Complete the sync request with deferral handling. R_RETURN(this->CompleteSyncRequest(session)); } Result ServerManager::CompleteSyncRequest(Session* session) { Result res = ResultSuccess; Result service_res = ResultSuccess; // Mark the request as not deferred. session->GetContext()->SetIsDeferred(false); // Complete the request. We have exclusive access to this session. auto* server_session = static_cast(session->GetNativeHandle()); service_res = session->GetManager()->CompleteSyncRequest(server_session, *session->GetContext()); // If we've been deferred, we're done. if (session->GetContext()->GetIsDeferred()) { // Insert into deferred session list. std::scoped_lock ll{m_deferred_list_mutex}; m_deferred_sessions.push_back(session); // Finish. R_SUCCEED(); } // Send the reply. res = server_session->SendReplyHLE(); // If the session has been closed, we're done. if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) { this->DestroySession(session); R_SUCCEED(); } R_ASSERT(res); R_ASSERT(service_res); // We succeeded, so we can process future messages on this session. this->LinkToDeferredList(session); R_SUCCEED(); } Result ServerManager::OnDeferralEvent() { // Clear event before grabbing the list. m_deferral_event->Clear(); // Get and clear list. const auto deferrals = [&] { std::scoped_lock lk{m_deferred_list_mutex}; return std::move(m_deferred_sessions); }(); // Relink deferral event. this->LinkToDeferredList(std::addressof(*m_deferral_holder)); // For each session, try again to complete the request. for (auto* session : deferrals) { R_ASSERT(this->CompleteSyncRequest(session)); } R_SUCCEED(); } void ServerManager::DestroySession(Session* session) { // Unlink. { std::scoped_lock lk{m_deferred_list_mutex}; m_sessions.erase(m_sessions.iterator_to(*session)); } // Free the session. delete session; } } // namespace Service