diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2dfdcb0d79..7fd2260505 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -170,6 +170,7 @@ add_library(core STATIC hle/kernel/server_port.h hle/kernel/server_session.cpp hle/kernel/server_session.h + hle/kernel/session.cpp hle/kernel/session.h hle/kernel/shared_memory.cpp hle/kernel/shared_memory.h diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index b9f9ae1fa5..0dc6a4a437 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -19,6 +19,7 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/session.h" #include "core/hle/result.h" namespace IPC { @@ -139,10 +140,9 @@ public: context->AddDomainObject(std::move(iface)); } else { auto& kernel = Core::System::GetInstance().Kernel(); - auto [server, client] = - Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName()); - iface->ClientConnected(server); + auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName()); context->AddMoveObject(std::move(client)); + iface->ClientConnected(std::move(server)); } } diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 4637b6017b..00bb939a0b 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -9,6 +9,7 @@ #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/session.h" namespace Kernel { @@ -20,28 +21,23 @@ std::shared_ptr ClientPort::GetServerPort() const { } ResultVal> ClientPort::Connect() { - // Note: Threads do not wait for the server endpoint to call - // AcceptSession before returning from this call. - if (active_sessions >= max_sessions) { return ERR_MAX_CONNECTIONS_REACHED; } active_sessions++; - // Create a new session pair, let the created sessions inherit the parent port's HLE handler. - auto [server, client] = - ServerSession::CreateSessionPair(kernel, server_port->GetName(), SharedFrom(this)); + auto [client, server] = Kernel::Session::Create(kernel, name); if (server_port->HasHLEHandler()) { - server_port->GetHLEHandler()->ClientConnected(server); + server_port->GetHLEHandler()->ClientConnected(std::move(server)); } else { - server_port->AppendPendingSession(server); + server_port->AppendPendingSession(std::move(server)); } // Wake the threads waiting on the ServerPort server_port->WakeupAllWaitingThreads(); - return MakeResult(client); + return MakeResult(std::move(client)); } void ClientPort::ConnectionClosed() { diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 9849dbe914..4669a14adc 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2019 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -12,22 +12,44 @@ namespace Kernel { -ClientSession::ClientSession(KernelCore& kernel) : Object{kernel} {} +ClientSession::ClientSession(KernelCore& kernel) : WaitObject{kernel} {} + ClientSession::~ClientSession() { // This destructor will be called automatically when the last ClientSession handle is closed by // the emulated application. - if (auto server = parent->server.lock()) { - server->ClientDisconnected(); + if (parent->Server()) { + parent->Server()->ClientDisconnected(); } } -ResultCode ClientSession::SendSyncRequest(Thread* thread, Memory::Memory& memory) { - // Signal the server session that new data is available - if (auto server = parent->server.lock()) { - return server->HandleSyncRequest(SharedFrom(thread), memory); +bool ClientSession::ShouldWait(const Thread* thread) const { + UNIMPLEMENTED(); + return {}; +} + +void ClientSession::Acquire(Thread* thread) { + UNIMPLEMENTED(); +} + +ResultVal> ClientSession::Create(KernelCore& kernel, + std::shared_ptr parent, + std::string name) { + std::shared_ptr client_session{std::make_shared(kernel)}; + + client_session->name = std::move(name); + client_session->parent = std::move(parent); + + return MakeResult(std::move(client_session)); +} + +ResultCode ClientSession::SendSyncRequest(std::shared_ptr thread, Memory::Memory& memory) { + // Keep ServerSession alive until we're done working with it. + if (!parent->Server()) { + return ERR_SESSION_CLOSED_BY_REMOTE; } - return ERR_SESSION_CLOSED_BY_REMOTE; + // Signal the server session that new data is available + return parent->Server()->HandleSyncRequest(std::move(thread), memory); } } // namespace Kernel diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 484dd7bc90..b4289a9a81 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2019 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -6,7 +6,9 @@ #include #include -#include "core/hle/kernel/object.h" + +#include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" union ResultCode; @@ -18,15 +20,14 @@ namespace Kernel { class KernelCore; class Session; -class ServerSession; class Thread; -class ClientSession final : public Object { +class ClientSession final : public WaitObject { public: explicit ClientSession(KernelCore& kernel); ~ClientSession() override; - friend class ServerSession; + friend class Session; std::string GetTypeName() const override { return "ClientSession"; @@ -41,9 +42,17 @@ public: return HANDLE_TYPE; } - ResultCode SendSyncRequest(Thread* thread, Memory::Memory& memory); + ResultCode SendSyncRequest(std::shared_ptr thread, Memory::Memory& memory); + + bool ShouldWait(const Thread* thread) const override; + + void Acquire(Thread* thread) override; private: + static ResultVal> Create(KernelCore& kernel, + std::shared_ptr parent, + std::string name = "Unknown"); + /// The parent session, which links to the server endpoint. std::shared_ptr parent; diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 8b01567a84..2db28dcf03 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -74,6 +74,8 @@ std::shared_ptr HLERequestContext::SleepClientThread( thread->WakeAfterDelay(timeout); } + is_thread_waiting = true; + return writable_event; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index dab37ba0d4..050ad8fd74 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -264,6 +264,18 @@ public: std::string Description() const; + Thread& GetThread() { + return *thread; + } + + const Thread& GetThread() const { + return *thread; + } + + bool IsThreadWaiting() const { + return is_thread_waiting; + } + private: void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); @@ -290,6 +302,7 @@ private: u32_le command{}; std::vector> domain_request_handlers; + bool is_thread_waiting{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp index 10431e94c1..2c571792b2 100644 --- a/src/core/hle/kernel/object.cpp +++ b/src/core/hle/kernel/object.cpp @@ -27,6 +27,7 @@ bool Object::IsWaitable() const { case HandleType::ResourceLimit: case HandleType::ClientPort: case HandleType::ClientSession: + case HandleType::Session: return false; } diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index bbbb4e7cce..e3391e2af8 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -29,6 +29,7 @@ enum class HandleType : u32 { ServerPort, ClientSession, ServerSession, + Session, }; class Object : NonCopyable, public std::enable_shared_from_this { diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 1198c7a970..7825e1ec40 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2019 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -24,34 +25,29 @@ namespace Kernel { ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {} -ServerSession::~ServerSession() { - // This destructor will be called automatically when the last ServerSession handle is closed by - // the emulated application. - - // Decrease the port's connection count. - if (parent->port) { - parent->port->ConnectionClosed(); - } -} +ServerSession::~ServerSession() = default; ResultVal> ServerSession::Create(KernelCore& kernel, + std::shared_ptr parent, std::string name) { - std::shared_ptr server_session = std::make_shared(kernel); + std::shared_ptr session{std::make_shared(kernel)}; - server_session->name = std::move(name); - server_session->parent = nullptr; + session->request_event = Core::Timing::CreateEvent( + name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); }); + session->name = std::move(name); + session->parent = std::move(parent); - return MakeResult(std::move(server_session)); + return MakeResult(std::move(session)); } bool ServerSession::ShouldWait(const Thread* thread) const { - // Wait if we have no pending requests, or if we're currently handling a request. - if (auto client = parent->client.lock()) { - return pending_requesting_threads.empty() || currently_handling != nullptr; + // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. + if (!parent->Client()) { + return false; } - // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. - return {}; + // Wait if we have no pending requests, or if we're currently handling a request. + return pending_requesting_threads.empty() || currently_handling != nullptr; } void ServerSession::Acquire(Thread* thread) { @@ -128,14 +124,21 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con return RESULT_SUCCESS; } -ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, - Memory::Memory& memory) { - // The ServerSession received a sync request, this means that there's new data available - // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or - // similar. - Kernel::HLERequestContext context(SharedFrom(this), thread); - u32* cmd_buf = (u32*)memory.GetPointer(thread->GetTLSAddress()); - context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); +ResultCode ServerSession::QueueSyncRequest(std::shared_ptr thread, Memory::Memory& memory) { + u32* cmd_buf{reinterpret_cast(memory.GetPointer(thread->GetTLSAddress()))}; + std::shared_ptr context{ + std::make_shared(SharedFrom(this), std::move(thread))}; + + context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); + request_queue.Push(std::move(context)); + + return RESULT_SUCCESS; +} + +ResultCode ServerSession::CompleteSyncRequest() { + ASSERT(!request_queue.Empty()); + + auto& context = *request_queue.Front(); ResultCode result = RESULT_SUCCESS; // If the session has been converted to a domain, handle the domain request @@ -147,61 +150,27 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, result = hle_handler->HandleSyncRequest(context); } - if (thread->GetStatus() == ThreadStatus::Running) { - // Put the thread to sleep until the server replies, it will be awoken in - // svcReplyAndReceive for LLE servers. - thread->SetStatus(ThreadStatus::WaitIPC); - - if (hle_handler != nullptr) { - // For HLE services, we put the request threads to sleep for a short duration to - // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for - // other reasons like an async callback. The IPC overhead is needed to prevent - // starvation when a thread only does sync requests to HLE services while a - // lower-priority thread is waiting to run. - - // This delay was approximated in a homebrew application by measuring the average time - // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC - // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have - // a high variance and vary between models. - static constexpr u64 IPCDelayNanoseconds = 39000; - thread->WakeAfterDelay(IPCDelayNanoseconds); - } else { - // Add the thread to the list of threads that have issued a sync request with this - // server. - pending_requesting_threads.push_back(std::move(thread)); - } - } - - // If this ServerSession does not have an HLE implementation, just wake up the threads waiting - // on it. - WakeupAllWaitingThreads(); - - // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the - // end of the command such that only commands following this one are handled as domains if (convert_to_domain) { ASSERT_MSG(IsSession(), "ServerSession is already a domain instance."); domain_request_handlers = {hle_handler}; convert_to_domain = false; } + // Some service requests require the thread to block + if (!context.IsThreadWaiting()) { + context.GetThread().ResumeFromWait(); + context.GetThread().SetWaitSynchronizationResult(result); + } + + request_queue.Pop(); + return result; } -ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel, - const std::string& name, - std::shared_ptr port) { - auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap(); - std::shared_ptr client_session = std::make_shared(kernel); - client_session->name = name + "_Client"; - - std::shared_ptr parent = std::make_shared(); - parent->client = client_session; - parent->server = server_session; - parent->port = std::move(port); - - client_session->parent = parent; - server_session->parent = parent; - - return std::make_pair(std::move(server_session), std::move(client_session)); +ResultCode ServerSession::HandleSyncRequest(std::shared_ptr thread, + Memory::Memory& memory) { + Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); + return QueueSyncRequest(std::move(thread), memory); } + } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 641709a45a..d6e48109e1 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2019 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -9,7 +9,7 @@ #include #include -#include "core/hle/kernel/object.h" +#include "common/threadsafe_queue.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" @@ -17,13 +17,14 @@ namespace Memory { class Memory; } +namespace Core::Timing { +struct EventType; +} + namespace Kernel { -class ClientPort; -class ClientSession; class HLERequestContext; class KernelCore; -class ServerSession; class Session; class SessionRequestHandler; class Thread; @@ -45,6 +46,12 @@ public: explicit ServerSession(KernelCore& kernel); ~ServerSession() override; + friend class Session; + + static ResultVal> Create(KernelCore& kernel, + std::shared_ptr parent, + std::string name = "Unknown"); + std::string GetTypeName() const override { return "ServerSession"; } @@ -66,18 +73,6 @@ public: return parent.get(); } - using SessionPair = std::pair, std::shared_ptr>; - - /** - * Creates a pair of ServerSession and an associated ClientSession. - * @param kernel The kernal instance to create the session pair under. - * @param name Optional name of the ports. - * @param client_port Optional The ClientPort that spawned this session. - * @return The created session tuple - */ - static SessionPair CreateSessionPair(KernelCore& kernel, const std::string& name = "Unknown", - std::shared_ptr client_port = nullptr); - /** * Sets the HLE handler for the session. This handler will be called to service IPC requests * instead of the regular IPC machinery. (The regular IPC machinery is currently not @@ -128,15 +123,11 @@ public: } private: - /** - * Creates a server session. The server session can have an optional HLE handler, - * which will be invoked to handle the IPC requests that this session receives. - * @param kernel The kernel instance to create this server session under. - * @param name Optional name of the server session. - * @return The created server session - */ - static ResultVal> Create(KernelCore& kernel, - std::string name = "Unknown"); + /// Queues a sync request from the emulated application. + ResultCode QueueSyncRequest(std::shared_ptr thread, Memory::Memory& memory); + + /// Completes a sync request from the emulated application. + ResultCode CompleteSyncRequest(); /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an /// object handle. @@ -166,6 +157,12 @@ private: /// The name of this session (optional) std::string name; + + /// Core timing event used to schedule the service request at some point in the future + std::shared_ptr request_event; + + /// Queue of scheduled service requests + Common::MPSCQueue> request_queue; }; } // namespace Kernel diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 6429147442..dee6e2b72e 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -1,12 +1,36 @@ -// Copyright 2015 Citra Emulator Project +// Copyright 2019 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" namespace Kernel { -Session::Session() {} -Session::~Session() {} +Session::Session(KernelCore& kernel) : WaitObject{kernel} {} +Session::~Session() = default; + +Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { + auto session{std::make_shared(kernel)}; + auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()}; + auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()}; + + session->name = std::move(name); + session->client = client_session; + session->server = server_session; + + return std::make_pair(std::move(client_session), std::move(server_session)); +} + +bool Session::ShouldWait(const Thread* thread) const { + UNIMPLEMENTED(); + return {}; +} + +void Session::Acquire(Thread* thread) { + UNIMPLEMENTED(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 94395f9f55..5a9d4e9ad6 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -1,27 +1,64 @@ -// Copyright 2018 yuzu emulator team +// Copyright 2019 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include "core/hle/kernel/object.h" +#include +#include + +#include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace Kernel { class ClientSession; -class ClientPort; class ServerSession; /** * Parent structure to link the client and server endpoints of a session with their associated - * client port. The client port need not exist, as is the case for portless sessions like the - * FS File and Directory sessions. When one of the endpoints of a session is destroyed, its - * corresponding field in this structure will be set to nullptr. + * client port. */ -class Session final { +class Session final : public WaitObject { public: - std::weak_ptr client; ///< The client endpoint of the session. - std::weak_ptr server; ///< The server endpoint of the session. - std::shared_ptr port; ///< The port that this session is associated with (optional). + explicit Session(KernelCore& kernel); + ~Session() override; + + using SessionPair = std::pair, std::shared_ptr>; + + static SessionPair Create(KernelCore& kernel, std::string name = "Unknown"); + + std::string GetName() const override { + return name; + } + + static constexpr HandleType HANDLE_TYPE = HandleType::Session; + HandleType GetHandleType() const override { + return HANDLE_TYPE; + } + + bool ShouldWait(const Thread* thread) const override; + + void Acquire(Thread* thread) override; + + std::shared_ptr Client() { + if (auto result{client.lock()}) { + return result; + } + return {}; + } + + std::shared_ptr Server() { + if (auto result{server.lock()}) { + return result; + } + return {}; + } + +private: + std::string name; + std::weak_ptr client; + std::weak_ptr server; }; + } // namespace Kernel diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index db3ae3eb88..bd25de478e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -381,11 +381,12 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - system.PrepareReschedule(); + auto thread = system.CurrentScheduler().GetCurrentThread(); + thread->InvalidateWakeupCallback(); + thread->SetStatus(ThreadStatus::WaitIPC); + system.PrepareReschedule(thread->GetProcessorID()); - // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server - // responds and cause a reschedule. - return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread(), system.Memory()); + return session->SendSyncRequest(SharedFrom(thread), system.Memory()); } /// Get the ID for the specified thread. diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index ec0367978c..4b79eb81d6 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -189,7 +189,7 @@ private: LOG_DEBUG(Service_NFP, "called"); auto nfc_event = nfp_interface.GetNFCEvent(); - if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { + if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) { device_state = DeviceState::TagFound; nfc_event->Clear(); } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5698f429f6..fa5347af99 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -186,7 +186,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co UNIMPLEMENTED_MSG("command_type={}", static_cast(context.GetCommandType())); } - context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread()); + context.WriteToOutgoingCommandBuffer(context.GetThread()); return RESULT_SUCCESS; } @@ -201,7 +201,7 @@ void Init(std::shared_ptr& sm, Core::System& system) { auto nv_flinger = std::make_shared(system); system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); - SM::ServiceManager::InstallInterfaces(sm); + SM::ServiceManager::InstallInterfaces(sm, system.Kernel()); Account::InstallInterfaces(system); AM::InstallInterfaces(*sm, nv_flinger, system); diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index af2fadcef4..c45b285f8f 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -30,10 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); - std::shared_ptr session{ctx.Session()->GetParent()->client}; - rb.PushMoveObjects(session); - - LOG_DEBUG(Service, "session={}", session->GetObjectId()); + rb.PushMoveObjects(ctx.Session()->GetParent()->Client()); } void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index a0a7206bb0..88909504da 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -36,10 +36,11 @@ static ResultCode ValidateServiceName(const std::string& name) { return RESULT_SUCCESS; } -void ServiceManager::InstallInterfaces(std::shared_ptr self) { +void ServiceManager::InstallInterfaces(std::shared_ptr self, + Kernel::KernelCore& kernel) { ASSERT(self->sm_interface.expired()); - auto sm = std::make_shared(self); + auto sm = std::make_shared(self, kernel); sm->InstallAsNamedPort(); self->sm_interface = sm; self->controller_interface = std::make_unique(); @@ -114,8 +115,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { std::string name(name_buf.begin(), end); - // TODO(yuriks): Permission checks go here - auto client_port = service_manager->GetServicePort(name); if (client_port.Failed()) { IPC::ResponseBuilder rb{ctx, 2}; @@ -127,14 +126,22 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { return; } - auto session = client_port.Unwrap()->Connect(); - ASSERT(session.Succeeded()); - if (session.Succeeded()) { - LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); - IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; - rb.Push(session.Code()); - rb.PushMoveObjects(std::move(session).Unwrap()); + auto [client, server] = Kernel::Session::Create(kernel, name); + + const auto& server_port = client_port.Unwrap()->GetServerPort(); + if (server_port->GetHLEHandler()) { + server_port->GetHLEHandler()->ClientConnected(server); + } else { + server_port->AppendPendingSession(server); } + + // Wake the threads waiting on the ServerPort + server_port->WakeupAllWaitingThreads(); + + LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; + rb.Push(RESULT_SUCCESS); + rb.PushMoveObjects(std::move(client)); } void SM::RegisterService(Kernel::HLERequestContext& ctx) { @@ -178,8 +185,8 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) { rb.Push(service_manager->UnregisterService(name)); } -SM::SM(std::shared_ptr service_manager) - : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) { +SM::SM(std::shared_ptr service_manager, Kernel::KernelCore& kernel) + : ServiceFramework{"sm:", 4}, service_manager{std::move(service_manager)}, kernel{kernel} { static const FunctionInfo functions[] = { {0x00000000, &SM::Initialize, "Initialize"}, {0x00000001, &SM::GetService, "GetService"}, diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 3de22268bb..b06d2f1030 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -18,6 +18,7 @@ namespace Kernel { class ClientPort; class ClientSession; +class KernelCore; class ServerPort; class SessionRequestHandler; } // namespace Kernel @@ -29,7 +30,7 @@ class Controller; /// Interface to "sm:" service class SM final : public ServiceFramework { public: - explicit SM(std::shared_ptr service_manager); + explicit SM(std::shared_ptr service_manager, Kernel::KernelCore& kernel); ~SM() override; private: @@ -39,11 +40,12 @@ private: void UnregisterService(Kernel::HLERequestContext& ctx); std::shared_ptr service_manager; + Kernel::KernelCore& kernel; }; class ServiceManager { public: - static void InstallInterfaces(std::shared_ptr self); + static void InstallInterfaces(std::shared_ptr self, Kernel::KernelCore& kernel); ServiceManager(); ~ServiceManager();